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