OpenVDB 14.0.0
Loading...
Searching...
No Matches
Utils.h
Go to the documentation of this file.
1// Copyright Contributors to the OpenVDB Project
2// SPDX-License-Identifier: Apache-2.0
3
4/// @file codegen/Utils.h
5///
6/// @authors Nick Avramoussis
7///
8/// @brief Utility code generation methods for performing various llvm
9/// operations
10///
11
12#ifndef OPENVDB_AX_CODEGEN_UTILS_HAS_BEEN_INCLUDED
13#define OPENVDB_AX_CODEGEN_UTILS_HAS_BEEN_INCLUDED
14
15#include "Types.h"
16
17#include "../ast/Tokens.h"
18#include "../Exceptions.h"
19
20#include <openvdb/version.h>
21#include <openvdb/util/Assert.h>
22
23#include <llvm/IR/IRBuilder.h>
24#include <llvm/IR/LLVMContext.h>
25#include <llvm/Support/raw_ostream.h> // llvm::errs()
26
27namespace openvdb {
29namespace OPENVDB_VERSION_NAME {
30
31namespace ax {
32namespace codegen {
33
34/// @note Function definitions for some types returned from automatic token to
35/// llvm IR operations. See llvmArithmeticConversion and llvmBianryConversion
36
37using CastFunction = std::function<llvm::Value*
38 (llvm::IRBuilder<>&, llvm::Value*, llvm::Type*)>;
39
40using BinaryFunction = std::function<llvm::Value*
41 (llvm::IRBuilder<>&, llvm::Value*, llvm::Value*)>;
42
43/// @brief Populate a vector of llvm Types from a vector of llvm values
44///
45/// @param values A vector of llvm values to retrieve types from
46/// @param types A vector of llvm types to populate
47///
48inline void
49valuesToTypes(const std::vector<llvm::Value*>& values,
50 std::vector<llvm::Type*>& types)
51{
52 types.reserve(values.size());
53 for (const auto& v : values) {
54 types.emplace_back(v->getType());
55 }
56}
57
58/// @brief Prints an llvm type to a std string
59///
60/// @param type The llvm type to convert
61/// @param str The string to store the type info to
62///
63inline void
64llvmTypeToString(const llvm::Type* const type, std::string& str)
65{
66 llvm::raw_string_ostream os(str);
67 type->print(os);
68 os.flush();
69}
70
71/// @brief Return an llvm value representing a pointer to the provided ptr builtin
72/// ValueT.
73/// @note This is probably not a suitable solution for anything other than POD
74/// types and should be used with caution.
75///
76/// @param ptr A pointer to a type of ValueT whose address will be computed and
77/// returned
78/// @param builder The current llvm IRBuilder
79///
80template <typename ValueT>
81inline llvm::Value*
82llvmPointerFromAddress(const ValueT* const& ptr,
83 llvm::IRBuilder<>& builder)
84{
85 llvm::Value* address =
86 llvm::ConstantInt::get(llvm::Type::getIntNTy(builder.getContext(), sizeof(uintptr_t)*8),
87 reinterpret_cast<uintptr_t>(ptr));
88 return builder.CreateIntToPtr(address, LLVMType<ValueT*>::get(builder.getContext()));
89}
90
91/// @brief Insert a stack allocation at the beginning of the current function
92/// of the provided type and size. The IRBuilder's insertion point must
93/// be set to a BasicBlock with a valid Function parent.
94/// @note If a size is provided, the size must not depend on any other
95/// instructions. If it does, invalid LLVM IR will bb generated.
96///
97/// @param B The IRBuilder
98/// @param type The type to allocate
99/// @param size Optional count of allocations. If nullptr, runs a single allocation
100inline llvm::Value*
101insertStaticAlloca(llvm::IRBuilder<>& B,
102 llvm::Type* type,
103 llvm::Value* size = nullptr)
104{
105 llvm::StructType* strtype = LLVMType<codegen::String>::get(B.getContext());
106 // Create the allocation at the start of the function block
107 llvm::Function* parent = B.GetInsertBlock()->getParent();
108 OPENVDB_ASSERT(parent && !parent->empty());
109 auto IP = B.saveIP();
110 llvm::BasicBlock& block = parent->front();
111 if (block.empty()) B.SetInsertPoint(&block);
112 else B.SetInsertPoint(&(block.front()));
113 llvm::Value* result = B.CreateAlloca(type, size);
114
115 /// @note Strings need to be initialised correctly when they are
116 /// created. We alloc them at the start of the function but
117 /// strings in branches may not ever be set to anything. If
118 /// we don't init these correctly, the clearup frees will
119 /// try and free uninitialised memory
120 if (type == strtype) {
121 llvm::Value* cptr = B.CreateStructGEP(strtype, result, 0); // char**
122 llvm::Value* sso = B.CreateStructGEP(strtype, result, 1); // char[]*
123 llvm::Value* sso_load = B.CreateConstGEP2_64(strtype->getTypeAtIndex(1), sso, 0, 0); // char[]
124 llvm::Value* len = B.CreateStructGEP(strtype, result, 2);
125 B.CreateStore(sso_load, cptr); // this->ptr = this->SSO;
126 B.CreateStore(B.getInt64(0), len);
127 }
128 B.restoreIP(IP);
129 return result;
130}
131
132inline llvm::Argument*
133extractArgument(llvm::Function* F, const size_t idx)
134{
135 if (!F) return nullptr;
136 if (idx >= F->arg_size()) return nullptr;
137 return llvm::cast<llvm::Argument>(F->arg_begin() + idx);
138}
139
140inline llvm::Argument*
141extractArgument(llvm::Function* F, const std::string& name)
142{
143 if (!F) return nullptr;
144 for (auto iter = F->arg_begin(); iter != F->arg_end(); ++iter) {
145 llvm::Argument* arg = llvm::cast<llvm::Argument>(iter);
146 if (arg->getName() == name) return arg;
147 }
148 return nullptr;
149}
150
151/// @brief Returns the highest order type from two LLVM Scalar types
152///
153/// @param typeA The first scalar llvm type
154/// @param typeB The second scalar llvm type
155///
156inline llvm::Type*
157typePrecedence(llvm::Type* const typeA,
158 llvm::Type* const typeB)
159{
160 OPENVDB_ASSERT(typeA && (typeA->isIntegerTy() || typeA->isFloatingPointTy()) &&
161 "First Type in typePrecedence is not a scalar type");
162 OPENVDB_ASSERT(typeB && (typeB->isIntegerTy() || typeB->isFloatingPointTy()) &&
163 "Second Type in typePrecedence is not a scalar type");
164
165 // handle implicit arithmetic conversion
166 // (http://osr507doc.sco.com/en/tools/clang_conv_implicit.html)
167
168 if (typeA->isDoubleTy()) return typeA;
169 if (typeB->isDoubleTy()) return typeB;
170
171 if (typeA->isFloatTy()) return typeA;
172 if (typeB->isFloatTy()) return typeB;
173
174 if (typeA->isIntegerTy(64)) return typeA;
175 if (typeB->isIntegerTy(64)) return typeB;
176
177 if (typeA->isIntegerTy(32)) return typeA;
178 if (typeB->isIntegerTy(32)) return typeB;
179
180 if (typeA->isIntegerTy(16)) return typeA;
181 if (typeB->isIntegerTy(16)) return typeB;
182
183 if (typeA->isIntegerTy(8)) return typeA;
184 if (typeB->isIntegerTy(8)) return typeB;
185
186 if (typeA->isIntegerTy(1)) return typeA;
187 if (typeB->isIntegerTy(1)) return typeB;
188
189 OPENVDB_ASSERT(false && "invalid LLVM type precedence");
190 return nullptr;
191}
192
193/// @brief Returns a CastFunction which represents the corresponding instruction
194/// to convert a source llvm Type to a target llvm Type. If the conversion
195/// is unsupported, throws an error.
196/// @warning This assumes any integer types are signed.
197/// @param sourceType The source type to cast
198/// @param targetType The target type to cast to
199/// @param twine An optional string description of the cast function. This can
200/// be used for for more verbose llvm information on IR compilation
201/// failure
202/// @todo Deprecate this method, move functionality into codegen::Value
203inline CastFunction
204llvmArithmeticConversion(const llvm::Type* const sourceType,
205 const llvm::Type* const targetType,
206 const std::string& twine = "")
207{
208
209#define BIND_ARITHMETIC_CAST_OP(Function, Twine) \
210 std::bind(&Function, \
211 std::placeholders::_1, \
212 std::placeholders::_2, \
213 std::placeholders::_3, \
214 Twine)
215
216#if LLVM_VERSION_MAJOR >= 19
217// For Trunc, assume argument is never NUW/NSW (last flags)
218#define BIND_ARITHMETIC_CAST_OP_TRUNC(Function, Twine) \
219 std::bind(&Function, \
220 std::placeholders::_1, \
221 std::placeholders::_2, \
222 std::placeholders::_3, \
223 Twine, \
224 /*IsNUW*/false, \
225 /*IsNSW*/false)
226#else
227#define BIND_ARITHMETIC_CAST_OP_TRUNC(Function, Twine) \
228 std::bind(&Function, \
229 std::placeholders::_1, \
230 std::placeholders::_2, \
231 std::placeholders::_3, \
232 Twine)
233#endif
234
235#if LLVM_VERSION_MAJOR >= 19
236// For UItoFP, assume argument is never negative (last flag)
237#define BIND_ARITHMETIC_CAST_OP_UIFP(Function, Twine) \
238 std::bind(&Function, \
239 std::placeholders::_1, \
240 std::placeholders::_2, \
241 std::placeholders::_3, \
242 Twine, \
243 false) // IsNonNeg
244#else
245#define BIND_ARITHMETIC_CAST_OP_UIFP(Function, Twine) \
246 std::bind(&Function, \
247 std::placeholders::_1, \
248 std::placeholders::_2, \
249 std::placeholders::_3, \
250 Twine)
251#endif
252
253// For Zext, assume argument is never negative (last flag)
254#define BIND_ARITHMETIC_CAST_OP_ZEXT(Function, Twine) \
255 std::bind(&Function, \
256 std::placeholders::_1, \
257 std::placeholders::_2, \
258 std::placeholders::_3, \
259 Twine, \
260 false) // IsNonNeg
261
262#if LLVM_VERSION_MAJOR >= 20
263#define BIND_ARITHMETIC_CAST_OP_FP(Function, Twine) \
264 std::bind(&Function, \
265 std::placeholders::_1, \
266 std::placeholders::_2, \
267 std::placeholders::_3, \
268 Twine, \
269 nullptr) // MDNode *FPMathTag
270#else
271#define BIND_ARITHMETIC_CAST_OP_FP(Function, Twine) \
272 std::bind(&Function, \
273 std::placeholders::_1, \
274 std::placeholders::_2, \
275 std::placeholders::_3, \
276 Twine)
277#endif
278
279 if (targetType->isDoubleTy()) {
280 if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP_FP(llvm::IRBuilder<>::CreateFPExt, twine);
281 else if (sourceType->isHalfTy()) return BIND_ARITHMETIC_CAST_OP_FP(llvm::IRBuilder<>::CreateFPExt, twine);
282 else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
283 else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
284 else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
285 else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
286 else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP_UIFP(llvm::IRBuilder<>::CreateUIToFP, twine);
287 }
288 else if (targetType->isFloatTy()) {
289 if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP_FP(llvm::IRBuilder<>::CreateFPTrunc, twine);
290 else if (sourceType->isHalfTy()) return BIND_ARITHMETIC_CAST_OP_FP(llvm::IRBuilder<>::CreateFPExt, twine);
291 else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
292 else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
293 else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
294 else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
295 else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP_UIFP(llvm::IRBuilder<>::CreateUIToFP, twine);
296 }
297 else if (targetType->isHalfTy()) {
298 if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP_FP(llvm::IRBuilder<>::CreateFPTrunc, twine);
299 else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP_FP(llvm::IRBuilder<>::CreateFPTrunc, twine);
300 else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
301 else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
302 else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
303 else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSIToFP, twine);
304 else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP_UIFP(llvm::IRBuilder<>::CreateUIToFP, twine);
305 }
306 else if (targetType->isIntegerTy(64)) {
307 if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
308 else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
309 else if (sourceType->isHalfTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
310 else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
311 else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
312 else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
313 else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP_ZEXT(llvm::IRBuilder<>::CreateZExt, twine);
314 }
315 else if (targetType->isIntegerTy(32)) {
316 if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
317 else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
318 else if (sourceType->isHalfTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
319 else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP_TRUNC(llvm::IRBuilder<>::CreateTrunc, twine);
320 else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
321 else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
322 else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP_ZEXT(llvm::IRBuilder<>::CreateZExt, twine);
323 }
324 else if (targetType->isIntegerTy(16)) {
325 if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
326 else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
327 else if (sourceType->isHalfTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
328 else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP_TRUNC(llvm::IRBuilder<>::CreateTrunc, twine);
329 else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP_TRUNC(llvm::IRBuilder<>::CreateTrunc, twine);
330 else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateSExt, twine);
331 else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP_ZEXT(llvm::IRBuilder<>::CreateZExt, twine);
332 }
333 else if (targetType->isIntegerTy(8)) {
334 if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
335 else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
336 else if (sourceType->isHalfTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToSI, twine);
337 else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP_TRUNC(llvm::IRBuilder<>::CreateTrunc, twine);
338 else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP_TRUNC(llvm::IRBuilder<>::CreateTrunc, twine);
339 else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP_TRUNC(llvm::IRBuilder<>::CreateTrunc, twine);
340 else if (sourceType->isIntegerTy(1)) return BIND_ARITHMETIC_CAST_OP_ZEXT(llvm::IRBuilder<>::CreateZExt, twine);
341 }
342 else if (targetType->isIntegerTy(1)) {
343 if (sourceType->isDoubleTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToUI, twine);
344 else if (sourceType->isFloatTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToUI, twine);
345 else if (sourceType->isHalfTy()) return BIND_ARITHMETIC_CAST_OP(llvm::IRBuilder<>::CreateFPToUI, twine);
346 else if (sourceType->isIntegerTy(64)) return BIND_ARITHMETIC_CAST_OP_TRUNC(llvm::IRBuilder<>::CreateTrunc, twine);
347 else if (sourceType->isIntegerTy(32)) return BIND_ARITHMETIC_CAST_OP_TRUNC(llvm::IRBuilder<>::CreateTrunc, twine);
348 else if (sourceType->isIntegerTy(16)) return BIND_ARITHMETIC_CAST_OP_TRUNC(llvm::IRBuilder<>::CreateTrunc, twine);
349 else if (sourceType->isIntegerTy(8)) return BIND_ARITHMETIC_CAST_OP_TRUNC(llvm::IRBuilder<>::CreateTrunc, twine);
350 }
351
352#undef BIND_ARITHMETIC_CAST_OP
353 OPENVDB_ASSERT(false && "invalid LLVM type conversion");
354 return CastFunction();
355}
356
357/// @brief Returns a BinaryFunction representing the corresponding instruction to
358/// perform on two scalar values, relative to a provided operator token. Note that
359/// not all operations are supported on floating point types! If the token is not
360/// supported, or the llvm type is not a scalar type, throws an error.
361/// @note Various default arguments are bound to provide a simple function call
362/// signature. For floating point operations, this includes a null pointer to
363/// the optional metadata node. For integer operations, this includes disabling
364/// all overflow/rounding optimisations
365///
366/// @param type The type defining the precision of the binary operation
367/// @param token The token used to create the relative binary operation
368/// @param twine An optional string description of the binary function. This can
369/// be used for for more verbose llvm information on IR compilation
370/// failure
371inline BinaryFunction
372llvmBinaryConversion(const llvm::Type* const type,
373 const ast::tokens::OperatorToken& token,
374 const std::string& twine = "")
375{
376
377#define BIND_BINARY_OP(Function) \
378 [twine](llvm::IRBuilder<>& B, llvm::Value* L, llvm::Value* R) \
379 -> llvm::Value* { return B.Function(L, R, twine); }
380
381 // NOTE: Binary % and / ops always take sign into account (CreateSDiv vs CreateUDiv, CreateSRem vs CreateURem).
382 // See http://stackoverflow.com/questions/5346160/llvm-irbuildercreateudiv-createsdiv-createexactudiv
383 // a%b in AX is implemented as a floored modulo op and is handled explicitly in binaryExpression
384
385 if (type->isFloatingPointTy()) {
388 && "unable to perform logical or bitwise operation on floating point values");
389
390 if (token == ast::tokens::PLUS) return BIND_BINARY_OP(CreateFAdd);
391 else if (token == ast::tokens::MINUS) return BIND_BINARY_OP(CreateFSub);
392 else if (token == ast::tokens::MULTIPLY) return BIND_BINARY_OP(CreateFMul);
393 else if (token == ast::tokens::DIVIDE) return BIND_BINARY_OP(CreateFDiv);
394 else if (token == ast::tokens::MODULO) return BIND_BINARY_OP(CreateFRem); // Note this is NOT a%b in AX.
395 else if (token == ast::tokens::EQUALSEQUALS) return BIND_BINARY_OP(CreateFCmpOEQ);
396 else if (token == ast::tokens::NOTEQUALS) return BIND_BINARY_OP(CreateFCmpONE);
397 else if (token == ast::tokens::MORETHAN) return BIND_BINARY_OP(CreateFCmpOGT);
398 else if (token == ast::tokens::LESSTHAN) return BIND_BINARY_OP(CreateFCmpOLT);
399 else if (token == ast::tokens::MORETHANOREQUAL) return BIND_BINARY_OP(CreateFCmpOGE);
400 else if (token == ast::tokens::LESSTHANOREQUAL) return BIND_BINARY_OP(CreateFCmpOLE);
401 OPENVDB_ASSERT(false && "unrecognised binary operator");
402 }
403 else if (type->isIntegerTy()) {
404 if (token == ast::tokens::PLUS) return BIND_BINARY_OP(CreateAdd); // No Unsigned/Signed Wrap
405 else if (token == ast::tokens::MINUS) return BIND_BINARY_OP(CreateSub); // No Unsigned/Signed Wrap
406 else if (token == ast::tokens::MULTIPLY) return BIND_BINARY_OP(CreateMul); // No Unsigned/Signed Wrap
407 else if (token == ast::tokens::DIVIDE) return BIND_BINARY_OP(CreateSDiv); // IsExact = false - when true, poison value if the reuslt is rounded
408 else if (token == ast::tokens::MODULO) return BIND_BINARY_OP(CreateSRem); // Note this is NOT a%b in AX.
409 else if (token == ast::tokens::EQUALSEQUALS) return BIND_BINARY_OP(CreateICmpEQ);
410 else if (token == ast::tokens::NOTEQUALS) return BIND_BINARY_OP(CreateICmpNE);
411 else if (token == ast::tokens::MORETHAN) return BIND_BINARY_OP(CreateICmpSGT);
412 else if (token == ast::tokens::LESSTHAN) return BIND_BINARY_OP(CreateICmpSLT);
413 else if (token == ast::tokens::MORETHANOREQUAL) return BIND_BINARY_OP(CreateICmpSGE);
414 else if (token == ast::tokens::LESSTHANOREQUAL) return BIND_BINARY_OP(CreateICmpSLE);
415 else if (token == ast::tokens::AND) return BIND_BINARY_OP(CreateAnd); // same as BITAND (bool/circuting logic handled a layer up)
416 else if (token == ast::tokens::OR) return BIND_BINARY_OP(CreateOr);
417 else if (token == ast::tokens::SHIFTLEFT) return BIND_BINARY_OP(CreateShl); // No Unsigned/Signed Wrap
418 else if (token == ast::tokens::SHIFTRIGHT) return BIND_BINARY_OP(CreateAShr); // IsExact = false - poison value if any of the bits shifted out are non-zero.
419 else if (token == ast::tokens::BITAND) return BIND_BINARY_OP(CreateAnd); // note same as AND
420 else if (token == ast::tokens::BITOR) return BIND_BINARY_OP(CreateOr);
421 else if (token == ast::tokens::BITXOR) return BIND_BINARY_OP(CreateXor);
422 OPENVDB_ASSERT(false && "unrecognised binary operator");
423 }
424
425#undef BIND_BINARY_OP
426 OPENVDB_ASSERT(false && "invalid LLVM type for binary operation");
427 return BinaryFunction();
428}
429
430/// @brief Returns true if the llvm Type 'from' can be safely cast to the llvm
431/// Type 'to'.
432inline bool isValidCast(llvm::Type* from, llvm::Type* to)
433{
434 OPENVDB_ASSERT(from && "llvm Type 'from' is null in isValidCast");
435 OPENVDB_ASSERT(to && "llvm Type 'to' is null in isValidCast");
436
437 if ((from->isIntegerTy() || from->isFloatingPointTy()) &&
438 (to->isIntegerTy() || to->isFloatingPointTy())) {
439 return true;
440 }
441 if (from->isArrayTy() && to->isArrayTy()) {
442 llvm::ArrayType* af = llvm::cast<llvm::ArrayType>(from);
443 llvm::ArrayType* at = llvm::cast<llvm::ArrayType>(to);
444 if (af->getArrayNumElements() == at->getArrayNumElements()) {
445 return isValidCast(af->getArrayElementType(),
446 at->getArrayElementType());
447 }
448 }
449 return false;
450}
451
452/// @brief Casts a scalar llvm Value to a target scalar llvm Type. Returns
453/// the cast scalar value of type targetType.
454/// @warning This assumes any integer types are signed.
455/// @param value A llvm scalar value to convert
456/// @param targetType The target llvm scalar type to convert to
457/// @param builder The current llvm IRBuilder
458inline llvm::Value*
459arithmeticConversion(llvm::Value* value,
460 llvm::Type* targetType,
461 llvm::IRBuilder<>& builder)
462{
463 OPENVDB_ASSERT(value && (value->getType()->isIntegerTy() || value->getType()->isFloatingPointTy()) &&
464 "First Value in arithmeticConversion is not a scalar type");
465 OPENVDB_ASSERT(targetType && (targetType->isIntegerTy() || targetType->isFloatingPointTy()) &&
466 "Target Type in arithmeticConversion is not a scalar type");
467
468 const llvm::Type* const valueType = value->getType();
469 if (valueType == targetType) return value;
470
471 CastFunction llvmCastFunction = llvmArithmeticConversion(valueType, targetType);
472 return llvmCastFunction(builder, value, targetType);
473}
474
475/// @brief Converts a vector of loaded llvm scalar values of the same type to a
476/// target scalar type. Each value is converted individually and the loaded
477/// result stored in the same location within values.
478/// @warning This assumes any integer types are signed.
479/// @param values A vector of llvm scalar values to convert
480/// @param targetElementType The target llvm scalar type to convert each value
481/// of the input vector
482/// @param builder The current llvm IRBuilder
483inline void
484arithmeticConversion(std::vector<llvm::Value*>& values,
485 llvm::Type* targetElementType,
486 llvm::IRBuilder<>& builder)
487{
488 OPENVDB_ASSERT(targetElementType && (targetElementType->isIntegerTy() ||
489 targetElementType->isFloatingPointTy()) &&
490 "Target element type is not a scalar type");
491
492 llvm::Type* sourceElementType = values.front()->getType();
493 OPENVDB_ASSERT(sourceElementType && (sourceElementType->isIntegerTy() ||
494 sourceElementType->isFloatingPointTy()) &&
495 "Source element type is not a scalar type");
496
497 if (sourceElementType == targetElementType) return;
498
499 CastFunction llvmCastFunction = llvmArithmeticConversion(sourceElementType, targetElementType);
500
501 for (llvm::Value*& value : values) {
502 value = llvmCastFunction(builder, value, targetElementType);
503 }
504}
505
506/// @brief Converts a vector of loaded llvm scalar values to the highest precision
507/// type stored amongst them. Any values which are not scalar types are ignored
508/// @warning This assumes any integer types are signed.
509/// @param values A vector of llvm scalar values to convert
510/// @param builder The current llvm IRBuilder
511inline void
512arithmeticConversion(std::vector<llvm::Value*>& values,
513 llvm::IRBuilder<>& builder)
514{
515 llvm::Type* typeCast = LLVMType<bool>::get(builder.getContext());
516 for (llvm::Value*& value : values) {
517 llvm::Type* type = value->getType();
518 if (type->isIntegerTy() || type->isFloatingPointTy()) {
519 typeCast = typePrecedence(typeCast, type);
520 }
521 }
522
523 arithmeticConversion(values, typeCast, builder);
524}
525
526/// @brief Chooses the highest order llvm Type as defined by typePrecedence
527/// from either of the two incoming values and casts the other value to
528/// the choosen type if it is not already. The types of valueA and valueB
529/// are guaranteed to match. Both values must be scalar LLVM types
530/// @warning This assumes any integer types are signed.
531/// @param valueA The first llvm value
532/// @param valueB The second llvm value
533/// @param builder The current llvm IRBuilder
534inline void
535arithmeticConversion(llvm::Value*& valueA,
536 llvm::Value*& valueB,
537 llvm::IRBuilder<>& builder)
538{
539 llvm::Type* type = typePrecedence(valueA->getType(), valueB->getType());
540 valueA = arithmeticConversion(valueA, type, builder);
541 valueB = arithmeticConversion(valueB, type, builder);
542}
543
544/// @brief Performs a C style boolean comparison from a given scalar LLVM value
545///
546/// @param value The scalar llvm value to convert to a boolean
547/// @param builder The current llvm IRBuilder
548///
549inline llvm::Value*
550boolComparison(llvm::Value* value,
551 llvm::IRBuilder<>& builder)
552{
553 llvm::Type* type = value->getType();
554
555 if (type->isFloatingPointTy()) return builder.CreateFCmpONE(value, llvm::ConstantFP::get(type, 0.0));
556 else if (type->isIntegerTy(1)) return builder.CreateICmpNE(value, llvm::ConstantInt::get(type, 0));
557 else if (type->isIntegerTy()) return builder.CreateICmpNE(value, llvm::ConstantInt::getSigned(type, 0));
558 OPENVDB_ASSERT(false && "Invalid type for bool conversion");
559 return nullptr;
560}
561
562/// @ brief Performs a binary operation on two loaded llvm scalar values of the same type.
563/// The type of operation performed is defined by the token (see the list of supported
564/// tokens in ast/Tokens.h. Returns a loaded llvm scalar result
565///
566/// @param lhs The left hand side value of the binary operation
567/// @param rhs The right hand side value of the binary operation
568/// @param token The token representing the binary operation to perform
569/// @param builder The current llvm IRBuilder
570inline llvm::Value*
571binaryOperator(llvm::Value* lhs, llvm::Value* rhs,
572 const ast::tokens::OperatorToken& token,
573 llvm::IRBuilder<>& builder)
574{
575 llvm::Type* lhsType = lhs->getType();
576 OPENVDB_ASSERT(lhsType == rhs->getType() ||
577 (token == ast::tokens::SHIFTLEFT ||
578 token == ast::tokens::SHIFTRIGHT));
579
581
582 if (opType == ast::tokens::LOGICAL) {
583 lhs = boolComparison(lhs, builder);
584 rhs = boolComparison(rhs, builder);
585 lhsType = lhs->getType(); // now bool type
586 }
587
588 const BinaryFunction llvmBinaryFunction = llvmBinaryConversion(lhsType, token);
589 return llvmBinaryFunction(builder, lhs, rhs);
590}
591
592} // namespace codegen
593} // namespace ax
594} // namespace OPENVDB_VERSION_NAME
595} // namespace openvdb
596
597#endif // OPENVDB_AX_CODEGEN_UTILS_HAS_BEEN_INCLUDED
598
#define OPENVDB_ASSERT(X)
Definition Assert.h:41
Various function and operator tokens used throughout the AST and code generation.
OpenVDB AX Exceptions.
Consolidated llvm types for most supported types.
OperatorToken
Definition Tokens.h:151
@ BITOR
Definition Tokens.h:181
@ DIVIDE
Definition Tokens.h:158
@ LESSTHANOREQUAL
Definition Tokens.h:174
@ SHIFTRIGHT
Definition Tokens.h:179
@ MORETHANOREQUAL
Definition Tokens.h:173
@ EQUALSEQUALS
RELATIONAL.
Definition Tokens.h:169
@ BITXOR
Definition Tokens.h:182
@ BITAND
Definition Tokens.h:180
@ AND
LOGICAL.
Definition Tokens.h:163
@ PLUS
ARITHMETIC.
Definition Tokens.h:155
@ LESSTHAN
Definition Tokens.h:172
@ OR
Definition Tokens.h:164
@ MODULO
Definition Tokens.h:159
@ MORETHAN
Definition Tokens.h:171
@ NOTEQUALS
Definition Tokens.h:170
@ MULTIPLY
Definition Tokens.h:157
@ SHIFTLEFT
BITWISE.
Definition Tokens.h:178
@ MINUS
Definition Tokens.h:156
OperatorType operatorType(const OperatorToken token)
Definition Tokens.h:210
OperatorType
Definition Tokens.h:201
@ LOGICAL
Definition Tokens.h:203
@ BITWISE
Definition Tokens.h:205
llvm::Value * llvmPointerFromAddress(const ValueT *const &ptr, llvm::IRBuilder<> &builder)
Return an llvm value representing a pointer to the provided ptr builtin ValueT.
Definition Utils.h:82
void valuesToTypes(const std::vector< llvm::Value * > &values, std::vector< llvm::Type * > &types)
Populate a vector of llvm Types from a vector of llvm values.
Definition Utils.h:49
llvm::Value * arithmeticConversion(llvm::Value *value, llvm::Type *targetType, llvm::IRBuilder<> &builder)
Casts a scalar llvm Value to a target scalar llvm Type. Returns the cast scalar value of type targetT...
Definition Utils.h:459
llvm::Value * insertStaticAlloca(llvm::IRBuilder<> &B, llvm::Type *type, llvm::Value *size=nullptr)
Insert a stack allocation at the beginning of the current function of the provided type and size....
Definition Utils.h:101
void llvmTypeToString(const llvm::Type *const type, std::string &str)
Prints an llvm type to a std string.
Definition Utils.h:64
llvm::Value * binaryOperator(llvm::Value *lhs, llvm::Value *rhs, const ast::tokens::OperatorToken &token, llvm::IRBuilder<> &builder)
Definition Utils.h:571
bool isValidCast(llvm::Type *from, llvm::Type *to)
Returns true if the llvm Type 'from' can be safely cast to the llvm Type 'to'.
Definition Utils.h:432
std::function< llvm::Value *(llvm::IRBuilder<> &, llvm::Value *, llvm::Type *)> CastFunction
Definition Utils.h:37
llvm::Argument * extractArgument(llvm::Function *F, const size_t idx)
Definition Utils.h:133
BinaryFunction llvmBinaryConversion(const llvm::Type *const type, const ast::tokens::OperatorToken &token, const std::string &twine="")
Returns a BinaryFunction representing the corresponding instruction to perform on two scalar values,...
Definition Utils.h:372
std::function< llvm::Value *(llvm::IRBuilder<> &, llvm::Value *, llvm::Value *)> BinaryFunction
Definition Utils.h:40
llvm::Type * typePrecedence(llvm::Type *const typeA, llvm::Type *const typeB)
Returns the highest order type from two LLVM Scalar types.
Definition Utils.h:157
llvm::Value * boolComparison(llvm::Value *value, llvm::IRBuilder<> &builder)
Performs a C style boolean comparison from a given scalar LLVM value.
Definition Utils.h:550
CastFunction llvmArithmeticConversion(const llvm::Type *const sourceType, const llvm::Type *const targetType, const std::string &twine="")
Returns a CastFunction which represents the corresponding instruction to convert a source llvm Type t...
Definition Utils.h:204
Definition Exceptions.h:13
#define BIND_ARITHMETIC_CAST_OP_FP(Function, Twine)
#define BIND_ARITHMETIC_CAST_OP_ZEXT(Function, Twine)
#define BIND_ARITHMETIC_CAST_OP_UIFP(Function, Twine)
#define BIND_ARITHMETIC_CAST_OP(Function, Twine)
#define BIND_ARITHMETIC_CAST_OP_TRUNC(Function, Twine)
#define BIND_BINARY_OP(Function)
static llvm::Type * get(llvm::LLVMContext &C)
Return an LLVM type which represents T.
Definition Types.h:81
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition version.h.in:121
#define OPENVDB_USE_VERSION_NAMESPACE
Definition version.h.in:291