GCC Code Coverage Report


Directory: ./
File: openvdb_ax/openvdb_ax/codegen/ComputeGenerator.cc
Date: 2022-07-25 17:40:05
Exec Total Coverage
Lines: 655 744 88.0%
Functions: 37 42 88.1%
Branches: 789 1302 60.6%

Line Branch Exec Source
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3
4 /// @file codegen/ComputeGenerator.cc
5
6 #include "ComputeGenerator.h"
7 #include "FunctionRegistry.h"
8 #include "FunctionTypes.h"
9 #include "Types.h"
10 #include "Utils.h"
11
12 #include "../ast/AST.h"
13 #include "../ast/Tokens.h"
14 #include "../compiler/CustomData.h"
15 #include "../Exceptions.h"
16
17 #include <llvm/ADT/SmallVector.h>
18 #include <llvm/IR/CallingConv.h>
19 #include <llvm/IR/Constants.h>
20 #include <llvm/IR/DerivedTypes.h>
21 #include <llvm/IR/GlobalVariable.h>
22 #include <llvm/IR/InlineAsm.h>
23 #include <llvm/IR/Instructions.h>
24 #include <llvm/IR/Intrinsics.h>
25 #include <llvm/Pass.h>
26 #include <llvm/Support/MathExtras.h>
27 #include <llvm/Support/raw_os_ostream.h>
28 #include <llvm/Transforms/Utils/BuildLibCalls.h>
29
30 namespace openvdb {
31 OPENVDB_USE_VERSION_NAMESPACE
32 namespace OPENVDB_VERSION_NAME {
33
34 namespace ax {
35 namespace codegen {
36
37 namespace {
38
39 inline void
40 printType(const llvm::Type* type, llvm::raw_os_ostream& stream, const bool axTypes)
41 {
42 const ast::tokens::CoreType token =
43 axTypes ? tokenFromLLVMType(type) : ast::tokens::UNKNOWN;
44 if (token == ast::tokens::UNKNOWN) type->print(stream);
45 else stream << ast::tokens::typeStringFromToken(token);
46 }
47
48 inline void
49 printTypes(llvm::raw_os_ostream& stream,
50 const std::vector<llvm::Type*>& types,
51 const std::vector<const char*>& names = {},
52 const std::string sep = "; ",
53 const bool axTypes = true)
54 {
55 if (types.empty()) return;
56 auto typeIter = types.cbegin();
57 std::vector<const char*>::const_iterator nameIter;
58 if (!names.empty()) nameIter = names.cbegin();
59
60 for (; typeIter != types.cend() - 1; ++typeIter) {
61 printType(*typeIter, stream, axTypes);
62 if (!names.empty() && nameIter != names.cend()) {
63 if (*nameIter && (*nameIter)[0] != '\0') {
64 stream << ' ' << *nameIter;
65 }
66 ++nameIter;
67 }
68 stream << sep;
69 }
70
71 printType(*typeIter, stream, axTypes);
72 if (!names.empty() && nameIter != names.cend()) {
73 if (*nameIter && (*nameIter)[0] != '\0') {
74 stream << ' ' << *nameIter;
75 }
76 }
77 }
78
79 }
80
81 const std::array<std::string, ComputeKernel::N_ARGS>&
82 281 ComputeKernel::getArgumentKeys()
83 {
84 static const std::array<std::string, ComputeKernel::N_ARGS> arguments = {
85 { "custom_data" }
86
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 280 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
281 };
87
88 281 return arguments;
89 }
90
91 562 std::string ComputeKernel::getDefaultName() { return "ax.compute"; }
92
93
94 ///////////////////////////////////////////////////////////////////////////
95 ///////////////////////////////////////////////////////////////////////////
96
97 namespace codegen_internal {
98
99 1816 ComputeGenerator::ComputeGenerator(llvm::Module& module,
100 const FunctionOptions& options,
101 FunctionRegistry& functionRegistry,
102 1816 Logger& logger)
103 : mModule(module)
104 , mContext(module.getContext())
105 , mBuilder(module.getContext())
106 , mValues()
107 , mBreakContinueStack()
108 , mScopeIndex(1)
109 , mSymbolTables()
110 , mFunction(nullptr)
111 , mOptions(options)
112 , mLog(logger)
113
2/4
✓ Branch 1 taken 1816 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1816 times.
✗ Branch 5 not taken.
3632 , mFunctionRegistry(functionRegistry) {}
114
115 281 bool ComputeGenerator::generate(const ast::Tree& tree)
116 {
117 llvm::FunctionType* type =
118 281 llvmFunctionTypeFromSignature<ComputeKernel::Signature>(mContext);
119
120
1/2
✓ Branch 2 taken 281 times.
✗ Branch 3 not taken.
281 mFunction = llvm::Function::Create(type,
121 llvm::Function::ExternalLinkage,
122 562 ComputeKernel::getDefaultName(),
123 281 &mModule);
124
125 // Set up arguments for initial entry
126
127
1/2
✓ Branch 0 taken 281 times.
✗ Branch 1 not taken.
281 llvm::Function::arg_iterator argIter = mFunction->arg_begin();
128 281 const auto arguments = ComputeKernel::getArgumentKeys();
129 auto keyIter = arguments.cbegin();
130
131
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 562 times.
✓ Branch 2 taken 281 times.
✓ Branch 3 taken 281 times.
562 for (; argIter != mFunction->arg_end(); ++argIter, ++keyIter) {
132
1/2
✓ Branch 2 taken 281 times.
✗ Branch 3 not taken.
281 argIter->setName(*keyIter);
133 }
134
135
1/2
✓ Branch 2 taken 281 times.
✗ Branch 3 not taken.
281 llvm::BasicBlock* entry = llvm::BasicBlock::Create(mContext,
136
3/8
✓ Branch 1 taken 281 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 281 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 281 times.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
562 "entry_" + ComputeKernel::getDefaultName(), mFunction);
137 mBuilder.SetInsertPoint(entry);
138
139 // if traverse is false, log should have error, but can error
140 // without stopping traversal, so check both
141
1/2
✓ Branch 1 taken 281 times.
✗ Branch 2 not taken.
281 const size_t err = mLog.errors();
142
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 281 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
281 if (!this->traverse(&tree) || (mLog.errors() > err)) return false;
143
144 // free strings at terminating blocks
145
146 this->createFreeSymbolStrings(mBuilder);
147
148 return true;
149 }
150
151 2395 bool ComputeGenerator::visit(const ast::Block* block)
152 {
153 2395 mScopeIndex++;
154
155 // traverse the contents of the block
156 const size_t children = block->children();
157
158
2/2
✓ Branch 0 taken 15552 times.
✓ Branch 1 taken 2103 times.
17655 for (size_t i = 0; i < children; ++i) {
159
4/4
✓ Branch 1 taken 299 times.
✓ Branch 2 taken 15244 times.
✓ Branch 4 taken 16 times.
✓ Branch 5 taken 283 times.
15552 if (!this->traverse(block->child(i)) && mLog.atErrorLimit()) {
160 return false;
161 }
162 // reset the value stack for each statement
163 15260 mValues = std::stack<llvm::Value*>();
164 }
165
166 2103 mSymbolTables.erase(mScopeIndex);
167 2103 mScopeIndex--;
168 2103 return true;
169 }
170
171 178 bool ComputeGenerator::visit(const ast::CommaOperator* comma)
172 {
173 // traverse the contents of the comma expression
174 const size_t children = comma->children();
175 178 llvm::Value* value = nullptr;
176 bool hasErrored = false;
177
2/2
✓ Branch 0 taken 451 times.
✓ Branch 1 taken 176 times.
627 for (size_t i = 0; i < children; ++i) {
178
2/2
✓ Branch 1 taken 449 times.
✓ Branch 2 taken 2 times.
451 if (this->traverse(comma->child(i))) {
179
1/2
✓ Branch 0 taken 449 times.
✗ Branch 1 not taken.
449 value = mValues.top(); mValues.pop();
180 }
181 else {
182
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (mLog.atErrorLimit()) return false;
183 hasErrored = true;
184 }
185 }
186 // only keep the last value
187
2/4
✓ Branch 0 taken 176 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 176 times.
✗ Branch 3 not taken.
176 if (!value || hasErrored) return false;
188 mValues.push(value);
189 return true;
190 }
191
192 173 bool ComputeGenerator::visit(const ast::ConditionalStatement* cond)
193 {
194 173 llvm::BasicBlock* postIfBlock = llvm::BasicBlock::Create(mContext, "block", mFunction);
195
2/2
✓ Branch 2 taken 50 times.
✓ Branch 3 taken 123 times.
173 llvm::BasicBlock* thenBlock = llvm::BasicBlock::Create(mContext, "then", mFunction);
196 const bool hasElse = cond->hasFalse();
197
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 123 times.
173 llvm::BasicBlock* elseBlock = hasElse ? llvm::BasicBlock::Create(mContext, "else", mFunction) : postIfBlock;
198
199 // generate conditional
200
2/2
✓ Branch 1 taken 171 times.
✓ Branch 2 taken 2 times.
173 if (this->traverse(cond->condition())) {
201
1/2
✓ Branch 0 taken 171 times.
✗ Branch 1 not taken.
171 llvm::Value* condition = mValues.top(); mValues.pop();
202
203
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 149 times.
171 if (condition->getType()->isPointerTy()) {
204 22 condition = mBuilder.CreateLoad(condition);
205 }
206 llvm::Type* conditionType = condition->getType();
207 // check the type of the condition branch is bool-convertable
208
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 153 times.
171 if (conditionType->isFloatingPointTy() || conditionType->isIntegerTy()) {
209 153 condition = boolComparison(condition, mBuilder);
210 153 mBuilder.CreateCondBr(condition, thenBlock, elseBlock);
211 } else {
212
4/6
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 12 times.
36 if (!mLog.error("cannot convert non-scalar type to bool in condition", cond->condition())) return false;
213 }
214
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 } else if (mLog.atErrorLimit()) return false;
215
216 // generate if-then branch
217 mBuilder.SetInsertPoint(thenBlock);
218
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (!this->traverse(cond->trueBranch()) && mLog.atErrorLimit()) return false;
219 159 mBuilder.CreateBr(postIfBlock);
220
221
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 109 times.
159 if (hasElse) {
222 // generate else-then branch
223 mBuilder.SetInsertPoint(elseBlock);
224 if (!this->traverse(cond->falseBranch()) && mLog.atErrorLimit()) return false;
225 50 mBuilder.CreateBr(postIfBlock);
226 }
227
228 // reset to continue block
229 mBuilder.SetInsertPoint(postIfBlock);
230
231 // reset the value stack
232 159 mValues = std::stack<llvm::Value*>();
233
234 159 return true;
235 }
236
237 184 bool ComputeGenerator::visit(const ast::TernaryOperator* tern)
238 {
239 184 llvm::BasicBlock* trueBlock = llvm::BasicBlock::Create(mContext, "ternary_true", mFunction);
240 184 llvm::BasicBlock* falseBlock = llvm::BasicBlock::Create(mContext, "ternary_false", mFunction);
241 184 llvm::BasicBlock* returnBlock = llvm::BasicBlock::Create(mContext, "ternary_return", mFunction);
242
243 llvm::Value* trueValue = nullptr;
244 llvm::Type* trueType = nullptr;
245 bool truePtr = false;
246 // generate conditional
247 184 bool conditionSuccess = this->traverse(tern->condition());
248
2/2
✓ Branch 0 taken 179 times.
✓ Branch 1 taken 5 times.
184 if (conditionSuccess) {
249 // get the condition
250
1/2
✓ Branch 0 taken 179 times.
✗ Branch 1 not taken.
179 trueValue = mValues.top(); mValues.pop();
251
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 179 times.
179 assert(trueValue);
252
253 trueType = trueValue->getType();
254 truePtr = trueType->isPointerTy();
255
256
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 140 times.
179 llvm::Type* conditionType = truePtr ? trueType->getPointerElementType() : trueType;
257 llvm::Value* boolCondition = nullptr;
258 // check the type of the condition branch is bool-convertable
259
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 162 times.
179 if (conditionType->isFloatingPointTy() || conditionType->isIntegerTy()) {
260
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 140 times.
162 boolCondition = truePtr ?
261 162 boolComparison(mBuilder.CreateLoad(trueValue), mBuilder) : boolComparison(trueValue, mBuilder);
262 162 mBuilder.CreateCondBr(boolCondition, trueBlock, falseBlock);
263 }
264 else {
265
4/6
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 14 times.
34 if (!mLog.error("cannot convert non-scalar type to bool in condition", tern->condition())) return false;
266 conditionSuccess = false;
267 }
268 }
269
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4 times.
5 else if (mLog.atErrorLimit()) return false;
270
271 // generate true branch, if it exists otherwise take condition as true value
272
273
2/2
✓ Branch 0 taken 152 times.
✓ Branch 1 taken 14 times.
166 mBuilder.SetInsertPoint(trueBlock);
274 bool trueSuccess = conditionSuccess;
275
2/2
✓ Branch 0 taken 152 times.
✓ Branch 1 taken 14 times.
166 if (tern->hasTrue()) {
276 152 trueSuccess = this->traverse(tern->trueBranch());
277
2/2
✓ Branch 0 taken 149 times.
✓ Branch 1 taken 3 times.
152 if (trueSuccess) {
278
1/2
✓ Branch 0 taken 149 times.
✗ Branch 1 not taken.
149 trueValue = mValues.top(); mValues.pop();// get true value from true expression
279 // update true type details
280 trueType = trueValue->getType();
281 }
282
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 else if (mLog.atErrorLimit()) return false;
283 }
284
285 163 llvm::BranchInst* trueBranch = mBuilder.CreateBr(returnBlock);
286
287 // generate false branch
288
289 mBuilder.SetInsertPoint(falseBlock);
290 163 bool falseSuccess = this->traverse(tern->falseBranch());
291 // even if the condition isnt successful but the others are, we continue to code gen to find type errors in branches
292
2/2
✓ Branch 0 taken 159 times.
✓ Branch 1 taken 4 times.
163 if (!(trueSuccess && falseSuccess)) return false;
293
294 159 llvm::BranchInst* falseBranch = mBuilder.CreateBr(returnBlock);
295
296
1/2
✓ Branch 0 taken 159 times.
✗ Branch 1 not taken.
159 llvm::Value* falseValue = mValues.top(); mValues.pop();
297
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 159 times.
159 llvm::Type* falseType = falseValue->getType();
298
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 159 times.
159 assert(trueType);
299 // if both variables of same type do no casting or loading
300
2/2
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 69 times.
159 if (trueType != falseType) {
301 // get the (contained) types of the expressions
302 truePtr = trueType->isPointerTy();
303
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 43 times.
90 if (truePtr) trueType = trueType->getPointerElementType();
304
305 const bool falsePtr = falseType->isPointerTy();
306
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 47 times.
90 if (falsePtr) falseType = falseType->getPointerElementType();
307
308 // if same contained type but one needs loading
309 // can only have one pointer, one not, for scalars right now, i.e. no loaded arrays or strings
310
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 78 times.
90 if (trueType == falseType) {
311
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 assert(!(truePtr && falsePtr));
312
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 4 times.
12 if (truePtr) {
313 8 mBuilder.SetInsertPoint(trueBranch);
314 8 trueValue = mBuilder.CreateLoad(trueValue);
315 }
316 else {
317 4 mBuilder.SetInsertPoint(falseBranch);
318 4 falseValue = mBuilder.CreateLoad(falseValue);
319 }
320 }
321 else { // needs casting
322
323 // get type for return
324 llvm::Type* returnType = nullptr;
325
326
2/2
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 16 times.
78 const bool trueScalar = (trueType->isIntegerTy() || trueType->isFloatingPointTy());
327
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 16 times.
46 if (trueScalar &&
328 (falseType->isIntegerTy() || falseType->isFloatingPointTy())) {
329 assert(trueType != falseType);
330 // SCALAR_SCALAR
331 20 returnType = typePrecedence(trueType, falseType);
332 // always load scalars here, even if they are the correct type
333 20 mBuilder.SetInsertPoint(trueBranch);
334
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 16 times.
20 if (truePtr) trueValue = mBuilder.CreateLoad(trueValue);
335 20 trueValue = arithmeticConversion(trueValue, returnType, mBuilder);
336 20 mBuilder.SetInsertPoint(falseBranch);
337
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 16 times.
20 if (falsePtr) falseValue = mBuilder.CreateLoad(falseValue);
338 20 falseValue = arithmeticConversion(falseValue, returnType, mBuilder);
339 }
340
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 24 times.
29 else if (trueType->isArrayTy() && falseType->isArrayTy()
341
4/4
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 29 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
63 && (trueType->getArrayNumElements() == falseType->getArrayNumElements())) {
342 // ARRAY_ARRAY
343 trueType = trueType->getArrayElementType();
344 falseType = falseType->getArrayElementType();
345 4 returnType = typePrecedence(trueType, falseType);
346
347
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (trueType != returnType) {
348 4 mBuilder.SetInsertPoint(trueBranch);
349 4 trueValue = arrayCast(trueValue, returnType, mBuilder);
350 }
351 else if (falseType != returnType) {
352 mBuilder.SetInsertPoint(falseBranch);
353 falseValue = arrayCast(falseValue, returnType, mBuilder);
354 }
355 }
356
4/4
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 24 times.
54 else if (trueScalar && falseType->isArrayTy()) {
357 // SCALAR_ARRAY
358 24 returnType = typePrecedence(trueType, falseType->getArrayElementType());
359 24 mBuilder.SetInsertPoint(trueBranch);
360
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 20 times.
24 if (truePtr) trueValue = mBuilder.CreateLoad(trueValue);
361 24 trueValue = arithmeticConversion(trueValue, returnType, mBuilder);
362 const size_t arraySize = falseType->getArrayNumElements();
363
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 16 times.
24 if (arraySize == 9 || arraySize == 16) {
364
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
12 trueValue = scalarToMatrix(trueValue, mBuilder, arraySize == 9 ? 3 : 4);
365 }
366 else {
367 16 trueValue = arrayPack(trueValue, mBuilder, arraySize);
368 }
369
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 4 times.
24 if (falseType->getArrayElementType() != returnType) {
370 20 mBuilder.SetInsertPoint(falseBranch);
371 20 falseValue = arrayCast(falseValue, returnType, mBuilder);
372 }
373 }
374
4/4
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 21 times.
✓ Branch 3 taken 4 times.
30 else if (trueType->isArrayTy() &&
375 (falseType->isIntegerTy() || falseType->isFloatingPointTy())) {
376 // ARRAY_SCALAR
377 24 returnType = typePrecedence(trueType->getArrayElementType(), falseType);
378
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 4 times.
24 if (trueType->getArrayElementType() != returnType) {
379 20 mBuilder.SetInsertPoint(trueBranch);
380 20 trueValue = arrayCast(trueValue, returnType, mBuilder);
381 }
382 24 mBuilder.SetInsertPoint(falseBranch);
383
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 20 times.
24 if (falsePtr) falseValue = mBuilder.CreateLoad(falseValue);
384 24 falseValue = arithmeticConversion(falseValue, returnType, mBuilder);
385 const size_t arraySize = trueType->getArrayNumElements();
386
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 16 times.
24 if (arraySize == 9 || arraySize == 16) {
387
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
12 falseValue = scalarToMatrix(falseValue, mBuilder, arraySize == 9 ? 3 : 4);
388 }
389 else {
390 16 falseValue = arrayPack(falseValue, mBuilder, arraySize);
391 }
392 }
393 else {
394
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
6 mLog.error("unsupported implicit cast in ternary operation",
395 tern->hasTrue() ? tern->trueBranch() : tern->falseBranch());
396 6 return false;
397 }
398 }
399 }
400
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 66 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
69 else if (trueType->isVoidTy() && falseType->isVoidTy()) {
401 // void type ternary acts like if-else statement
402 // push void value to stop use of return from this expression
403 mBuilder.SetInsertPoint(returnBlock);
404 mValues.push(falseValue);
405
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 return conditionSuccess && trueSuccess && falseSuccess;
406 }
407
408 // reset to continue block
409 mBuilder.SetInsertPoint(returnBlock);
410 150 llvm::PHINode* ternary = mBuilder.CreatePHI(trueValue->getType(), 2, "ternary");
411
412 // if nesting branches the blocks for true and false branches may have been updated
413 // so get these again rather than reusing trueBlock/falseBlock
414 150 ternary->addIncoming(trueValue, trueBranch->getParent());
415 150 ternary->addIncoming(falseValue, falseBranch->getParent());
416
417 150 mValues.push(ternary);
418
3/4
✓ Branch 0 taken 148 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 148 times.
150 return conditionSuccess && trueSuccess && falseSuccess;
419 }
420
421 200 bool ComputeGenerator::visit(const ast::Loop* loop)
422 {
423 200 mScopeIndex++;
424
425 200 llvm::BasicBlock* postLoopBlock = llvm::BasicBlock::Create(mContext, "block", mFunction);
426 200 llvm::BasicBlock* conditionBlock = llvm::BasicBlock::Create(mContext, "loop_condition", mFunction);
427
2/2
✓ Branch 2 taken 41 times.
✓ Branch 3 taken 159 times.
200 llvm::BasicBlock* bodyBlock = llvm::BasicBlock::Create(mContext, "loop_body", mFunction);
428
429 llvm::BasicBlock* postBodyBlock = conditionBlock;
430
431 const ast::tokens::LoopToken loopType = loop->loopType();
432
3/4
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 159 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 41 times.
200 assert((loopType == ast::tokens::LoopToken::FOR ||
433 loopType == ast::tokens::LoopToken::WHILE ||
434 loopType == ast::tokens::LoopToken::DO) &&
435 "Unsupported loop type");
436
437
2/2
✓ Branch 0 taken 118 times.
✓ Branch 1 taken 82 times.
200 if (loopType == ast::tokens::LoopToken::FOR) {
438 // init -> condition -> body -> iter -> condition ... continue
439
440 // generate initial statement
441
2/2
✓ Branch 0 taken 94 times.
✓ Branch 1 taken 24 times.
118 if (loop->hasInit()) {
442
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 94 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
94 if (!this->traverse(loop->initial()) && mLog.atErrorLimit()) return false;
443 // reset the value stack
444 94 mValues = std::stack<llvm::Value*>();
445 }
446 118 mBuilder.CreateBr(conditionBlock);
447
448 // generate iteration
449
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 20 times.
118 if (loop->hasIter()) {
450 98 llvm::BasicBlock* iterBlock = llvm::BasicBlock::Create(mContext, "loop_iteration", mFunction);
451 postBodyBlock = iterBlock;
452
453 mBuilder.SetInsertPoint(iterBlock);
454
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 98 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
98 if (!this->traverse(loop->iteration()) && mLog.atErrorLimit()) return false;
455 98 mBuilder.CreateBr(conditionBlock);
456 }
457 }
458
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 41 times.
82 else if (loopType == ast::tokens::LoopToken::DO) {
459 // body -> condition -> body -> condition ... continue
460 41 mBuilder.CreateBr(bodyBlock);
461 }
462
1/2
✓ Branch 0 taken 41 times.
✗ Branch 1 not taken.
41 else if (loopType == ast::tokens::LoopToken::WHILE) {
463 // condition -> body -> condition ... continue
464 41 mBuilder.CreateBr(conditionBlock);
465 }
466
467 // store the destinations for break and continue
468
1/2
✓ Branch 0 taken 200 times.
✗ Branch 1 not taken.
200 mBreakContinueStack.push({postLoopBlock, postBodyBlock});
469
470 // generate loop body
471 mBuilder.SetInsertPoint(bodyBlock);
472 if (!this->traverse(loop->body()) && mLog.atErrorLimit()) return false;
473 200 mBuilder.CreateBr(postBodyBlock);
474
475 // generate condition
476 mBuilder.SetInsertPoint(conditionBlock);
477
2/2
✓ Branch 1 taken 199 times.
✓ Branch 2 taken 1 times.
200 if (this->traverse(loop->condition())) {
478
1/2
✓ Branch 0 taken 199 times.
✗ Branch 1 not taken.
199 llvm::Value* condition = mValues.top(); mValues.pop();
479
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 148 times.
199 if (condition->getType()->isPointerTy()) {
480 51 condition = mBuilder.CreateLoad(condition);
481 }
482 llvm::Type* conditionType = condition->getType();
483 // check the type of the condition branch is bool-convertable
484
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 160 times.
199 if (conditionType->isFloatingPointTy() || conditionType->isIntegerTy()) {
485 160 condition = boolComparison(condition, mBuilder);
486 160 mBuilder.CreateCondBr(condition, bodyBlock, postLoopBlock);
487 }
488 else {
489
4/6
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 36 times.
78 if (!mLog.error("cannot convert non-scalar type to bool in condition", loop->condition())) return false;
490 }
491 // reset the value stack
492 163 mValues = std::stack<llvm::Value*>();
493 }
494
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 else if (mLog.atErrorLimit()) return false;
495
496 // reset to post loop block
497 mBuilder.SetInsertPoint(postLoopBlock);
498
499 // discard break and continue
500 mBreakContinueStack.pop();
501
502 // remove the symbol table created in this scope
503 164 mSymbolTables.erase(mScopeIndex);
504 164 mScopeIndex--;
505
506 // reset the value stack
507 164 mValues = std::stack<llvm::Value*>();
508
509 164 return true;
510 }
511
512
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 89 times.
89 bool ComputeGenerator::visit(const ast::Keyword* node)
513 {
514 const ast::tokens::KeywordToken keyw = node->keyword();
515
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 89 times.
89 assert((keyw == ast::tokens::KeywordToken::RETURN ||
516 keyw == ast::tokens::KeywordToken::BREAK ||
517 keyw == ast::tokens::KeywordToken::CONTINUE) &&
518 "Unsupported keyword");
519
520
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 57 times.
89 if (keyw == ast::tokens::KeywordToken::RETURN) {
521 32 mBuilder.CreateRetVoid();
522 }
523
1/2
✓ Branch 0 taken 57 times.
✗ Branch 1 not taken.
57 else if (keyw == ast::tokens::KeywordToken::BREAK ||
524 keyw == ast::tokens::KeywordToken::CONTINUE) {
525 // find the parent loop, if it exists
526 const ast::Node* child = node;
527 const ast::Node* parentLoop = node->parent();
528
2/2
✓ Branch 0 taken 226 times.
✓ Branch 1 taken 1 times.
227 while (parentLoop) {
529
2/2
✓ Branch 1 taken 170 times.
✓ Branch 2 taken 56 times.
226 if (parentLoop->nodetype() == ast::Node::NodeType::LoopNode) {
530 break;
531 }
532 child = parentLoop;
533 parentLoop = child->parent();
534 }
535
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 56 times.
57 if (!parentLoop) {
536
3/6
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
3 if (!mLog.error("keyword \"" + ast::tokens::keywordNameFromToken(keyw)
537
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 + "\" used outside of loop.", node)) return false;
538 }
539 else {
540 const std::pair<llvm::BasicBlock*, llvm::BasicBlock*>
541 56 breakContinue = mBreakContinueStack.top();
542
543
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 16 times.
56 if (keyw == ast::tokens::KeywordToken::BREAK) {
544
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40 times.
40 assert(breakContinue.first);
545 40 mBuilder.CreateBr(breakContinue.first);
546 }
547
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 else if (keyw == ast::tokens::KeywordToken::CONTINUE) {
548
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 assert(breakContinue.second);
549 16 mBuilder.CreateBr(breakContinue.second);
550 }
551 }
552 }
553
554 88 llvm::BasicBlock* nullBlock = llvm::BasicBlock::Create(mContext, "null", mFunction);
555 // insert all remaining instructions in scope into a null block
556 // this will incorporate all instructions that follow until new insert point is set
557 mBuilder.SetInsertPoint(nullBlock);
558 88 return true;
559 }
560
561
2/2
✓ Branch 0 taken 115 times.
✓ Branch 1 taken 1729 times.
1844 bool ComputeGenerator::visit(const ast::BinaryOperator* node)
562 {
563 openvdb::ax::ast::tokens::OperatorToken opToken = node->operation();
564 // if AND or OR, need to handle short-circuiting
565 1844 if (opToken == openvdb::ax::ast::tokens::OperatorToken::AND
566
2/2
✓ Branch 0 taken 115 times.
✓ Branch 1 taken 1729 times.
1844 || opToken == openvdb::ax::ast::tokens::OperatorToken::OR) {
567 llvm::BranchInst* lhsBranch = nullptr;
568 115 llvm::BasicBlock* rhsBlock = llvm::BasicBlock::Create(mContext, "binary_rhs", mFunction);
569 115 llvm::BasicBlock* returnBlock = llvm::BasicBlock::Create(mContext, "binary_return", mFunction);
570 llvm::Value* lhs = nullptr;
571 115 bool lhsSuccess = this->traverse(node->lhs());
572
2/2
✓ Branch 0 taken 113 times.
✓ Branch 1 taken 2 times.
115 if (lhsSuccess) {
573
1/2
✓ Branch 0 taken 113 times.
✗ Branch 1 not taken.
113 lhs = mValues.top(); mValues.pop();
574 llvm::Type* lhsType = lhs->getType();
575
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 73 times.
113 if (lhsType->isPointerTy()) {
576
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 40 times.
40 lhs = mBuilder.CreateLoad(lhs);
577 lhsType = lhsType->getPointerElementType();
578 }
579
580
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 65 times.
97 if (lhsType->isFloatingPointTy() || lhsType->isIntegerTy()) {
581 81 lhs = boolComparison(lhs, mBuilder);
582
583
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 40 times.
81 if (opToken == openvdb::ax::ast::tokens::OperatorToken::AND) {
584 41 lhsBranch = mBuilder.CreateCondBr(lhs, rhsBlock, returnBlock);
585 }
586 else {
587 40 lhsBranch = mBuilder.CreateCondBr(lhs, returnBlock, rhsBlock);
588 }
589 }
590 else {
591
2/4
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 32 times.
✗ Branch 5 not taken.
64 mLog.error("cannot convert non-scalar lhs to bool", node->lhs());
592 lhsSuccess = false;
593 }
594 }
595
596
2/2
✓ Branch 1 taken 81 times.
✓ Branch 2 taken 34 times.
115 if (mLog.atErrorLimit()) return false;
597
598 mBuilder.SetInsertPoint(rhsBlock);
599 81 bool rhsSuccess = this->traverse(node->rhs());
600
2/2
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 1 times.
81 if (rhsSuccess) {
601
1/2
✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
80 llvm::Value* rhs = mValues.top(); mValues.pop();
602 llvm::Type* rhsType = rhs->getType();
603
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 48 times.
80 if (rhsType->isPointerTy()) {
604
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 32 times.
32 rhs = mBuilder.CreateLoad(rhs);
605 rhsType = rhsType->getPointerElementType();
606 }
607
608
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
64 if (rhsType->isFloatingPointTy() || rhsType->isIntegerTy()) {
609 80 rhs = boolComparison(rhs, mBuilder);
610 80 llvm::BranchInst* rhsBranch = mBuilder.CreateBr(returnBlock);
611
612 mBuilder.SetInsertPoint(returnBlock);
613
1/2
✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
80 if (lhsBranch) {// i.e. lhs was successful
614
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
80 assert(rhs && lhs);
615 80 llvm::PHINode* result = mBuilder.CreatePHI(LLVMType<bool>::get(mContext), 2, "binary_op");
616 80 result->addIncoming(lhs, lhsBranch->getParent());
617 80 result->addIncoming(rhs, rhsBranch->getParent());
618 80 mValues.push(result);
619 }
620 }
621 else {
622 mLog.error("cannot convert non-scalar rhs to bool", node->rhs());
623 rhsSuccess = false;
624 }
625 }
626 81 return lhsSuccess && rhsSuccess;
627 }
628 else {
629 llvm::Value* lhs = nullptr;
630
1/2
✓ Branch 1 taken 1729 times.
✗ Branch 2 not taken.
1729 if (this->traverse(node->lhs())) {
631
1/2
✓ Branch 0 taken 1729 times.
✗ Branch 1 not taken.
1729 lhs = mValues.top(); mValues.pop();
632 }
633
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
90 else if (mLog.atErrorLimit()) return false;
634 llvm::Value* rhs = nullptr;
635
1/2
✓ Branch 1 taken 1729 times.
✗ Branch 2 not taken.
1729 if (this->traverse(node->rhs())) {
636
1/2
✓ Branch 0 taken 1729 times.
✗ Branch 1 not taken.
1729 rhs = mValues.top(); mValues.pop();
637 }
638 else if (mLog.atErrorLimit()) return false;
639 1729 llvm::Value* result = nullptr;
640
3/4
✓ Branch 0 taken 1729 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1639 times.
✓ Branch 4 taken 90 times.
1729 if (!(lhs && rhs) || !this->binaryExpression(result, lhs, rhs, node->operation(), node)) return false;
641
642
1/2
✓ Branch 0 taken 1639 times.
✗ Branch 1 not taken.
1639 if (result) {
643 mValues.push(result);
644 }
645 }
646 1639 return true;
647 }
648
649
2/2
✓ Branch 0 taken 3602 times.
✓ Branch 1 taken 80 times.
3682 bool ComputeGenerator::visit(const ast::UnaryOperator* node)
650 {
651 // If the unary operation is a +, keep the value ptr on the stack and
652 // continue (avoid any new allocations or unecessary loads)
653
654 const ast::tokens::OperatorToken token = node->operation();
655
2/2
✓ Branch 0 taken 3602 times.
✓ Branch 1 taken 80 times.
3682 if (token == ast::tokens::PLUS) return true;
656
657 3602 if (token != ast::tokens::MINUS &&
658
3/4
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 3568 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 34 times.
3602 token != ast::tokens::BITNOT &&
659 token != ast::tokens::NOT) {
660 mLog.error("unrecognised unary operator \"" +
661 ast::tokens::operatorNameFromToken(token) + "\"", node);
662 return false;
663 }
664 // unary operator uses default traversal so value should be on the stack
665
2/2
✓ Branch 0 taken 157 times.
✓ Branch 1 taken 3445 times.
3602 llvm::Value* value = mValues.top();
666 llvm::Type* type = value->getType();
667
2/2
✓ Branch 0 taken 157 times.
✓ Branch 1 taken 3445 times.
3602 if (type->isPointerTy()) {
668 type = type->getPointerElementType();
669
2/2
✓ Branch 0 taken 121 times.
✓ Branch 1 taken 36 times.
157 if (type->isIntegerTy() || type->isFloatingPointTy()) {
670 48 value = mBuilder.CreateLoad(value);
671 }
672 }
673
674
2/2
✓ Branch 0 taken 476 times.
✓ Branch 1 taken 3126 times.
3602 llvm::Value* result = nullptr;
675
2/2
✓ Branch 0 taken 476 times.
✓ Branch 1 taken 3126 times.
3602 if (type->isIntegerTy()) {
676
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 460 times.
476 if (token == ast::tokens::NOT) {
677
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
32 if (type->isIntegerTy(1)) result = mBuilder.CreateICmpEQ(value, llvm::ConstantInt::get(type, 0));
678 else result = mBuilder.CreateICmpEQ(value, llvm::ConstantInt::getSigned(type, 0));
679 }
680 else {
681 // if bool, cast to int32 for unary minus and bitnot
682
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 460 times.
460 if (type->isIntegerTy(1)) {
683 type = LLVMType<int32_t>::get(mContext);
684 value = arithmeticConversion(value, type, mBuilder);
685 }
686
2/2
✓ Branch 0 taken 452 times.
✓ Branch 1 taken 8 times.
460 if (token == ast::tokens::MINUS) result = mBuilder.CreateNeg(value);
687
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 else if (token == ast::tokens::BITNOT) result = mBuilder.CreateNot(value);
688 }
689 }
690 else if (type->isFloatingPointTy()) {
691
1/2
✓ Branch 0 taken 3017 times.
✗ Branch 1 not taken.
3017 if (token == ast::tokens::MINUS) result = mBuilder.CreateFNeg(value);
692 else if (token == ast::tokens::NOT) result = mBuilder.CreateFCmpOEQ(value, llvm::ConstantFP::get(type, 0));
693 else if (token == ast::tokens::BITNOT) {
694 mLog.error("unable to perform operation \""
695 + ast::tokens::operatorNameFromToken(token) + "\" on floating point values", node);
696 return false;
697 }
698 }
699
1/2
✓ Branch 0 taken 109 times.
✗ Branch 1 not taken.
109 else if (type->isArrayTy()) {
700 type = type->getArrayElementType();
701 std::vector<llvm::Value*> elements;
702
1/2
✓ Branch 1 taken 109 times.
✗ Branch 2 not taken.
109 arrayUnpack(value, elements, mBuilder, /*load*/true);
703
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 109 times.
109 assert(elements.size() > 0);
704
705
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 85 times.
109 if (type->isIntegerTy()) {
706
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 16 times.
24 if (token == ast::tokens::MINUS) {
707
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 8 times.
36 for (llvm::Value*& element : elements) {
708
1/2
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 element = mBuilder.CreateNeg(element);
709 }
710 }
711
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 else if (token == ast::tokens::NOT) {
712
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 8 times.
36 for (llvm::Value*& element : elements) {
713
1/2
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
56 element = mBuilder.CreateICmpEQ(element,
714
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 llvm::ConstantInt::getSigned(type, 0));
715 }
716 }
717
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 else if (token == ast::tokens::BITNOT) {
718
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 8 times.
36 for (llvm::Value*& element : elements) {
719
1/2
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 element = mBuilder.CreateNot(element);
720 }
721 }
722 }
723 else if (type->isFloatingPointTy()) {
724
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 21 times.
85 if (token == ast::tokens::MINUS) {
725
2/2
✓ Branch 0 taken 656 times.
✓ Branch 1 taken 64 times.
720 for (llvm::Value*& element : elements) {
726
1/2
✓ Branch 2 taken 656 times.
✗ Branch 3 not taken.
656 element = mBuilder.CreateFNeg(element);
727 }
728 }
729 else {
730 //@todo support NOT?
731
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 mLog.error("unable to perform operation \""
732
3/8
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 21 times.
✗ Branch 8 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
42 + ast::tokens::operatorNameFromToken(token) + "\" on arrays/vectors", node);
733 21 return false;
734 }
735 }
736 else {
737 mLog.error("unrecognised array element type", node);
738 return false;
739 }
740
741
2/4
✓ Branch 1 taken 88 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 88 times.
✗ Branch 4 not taken.
88 result = arrayPack(elements, mBuilder);
742 }
743 else {
744 mLog.error("value is not a scalar or vector", node);
745 return false;
746 }
747
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3581 times.
3581 assert(result);
748 mValues.pop();
749 mValues.push(result);
750 return true;
751 }
752
753
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11448 times.
11448 bool ComputeGenerator::visit(const ast::AssignExpression* assign)
754 {
755 // default traversal, should have rhs and lhs on stack
756 // leave LHS on stack
757
1/2
✓ Branch 0 taken 11448 times.
✗ Branch 1 not taken.
11448 llvm::Value* rhs = mValues.top(); mValues.pop();
758 11448 llvm::Value* lhs = mValues.top();
759
760
2/2
✓ Branch 0 taken 1575 times.
✓ Branch 1 taken 9873 times.
11448 llvm::Type* rhsType = rhs->getType();
761
2/2
✓ Branch 0 taken 1575 times.
✓ Branch 1 taken 9873 times.
11448 if (assign->isCompound()) {
762 1575 llvm::Value* rhsValue = nullptr;
763
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1575 times.
1575 if (!this->binaryExpression(rhsValue, lhs, rhs, assign->operation(), assign)) return false;
764
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1575 times.
1575 assert(rhsValue);
765 1575 rhs = rhsValue;
766 rhsType = rhs->getType();
767 }
768 // rhs must be loaded for assignExpression() if it's a scalar
769
2/2
✓ Branch 0 taken 5055 times.
✓ Branch 1 taken 6393 times.
11448 if (rhsType->isPointerTy()) {
770 rhsType = rhsType->getPointerElementType();
771
2/2
✓ Branch 0 taken 4522 times.
✓ Branch 1 taken 533 times.
5055 if (rhsType->isIntegerTy() || rhsType->isFloatingPointTy()) {
772 1517 rhs = mBuilder.CreateLoad(rhs);
773 }
774 }
775
776
2/2
✓ Branch 1 taken 29 times.
✓ Branch 2 taken 11419 times.
11448 if (!this->assignExpression(lhs, rhs, assign)) return false;
777 return true;
778 }
779
780
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 774 times.
774 bool ComputeGenerator::visit(const ast::Crement* node)
781 {
782
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 774 times.
774 llvm::Value* value = mValues.top();
783
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 774 times.
774 if (!value->getType()->isPointerTy()) {
784 mLog.error("unable to assign to an rvalue", node);
785 return false;
786 }
787 774 llvm::Value* rvalue = mBuilder.CreateLoad(value);
788 llvm::Type* type = rvalue->getType();
789
790
3/4
✓ Branch 1 taken 774 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 320 times.
✓ Branch 4 taken 454 times.
774 if (type->isIntegerTy(1) || (!type->isIntegerTy() && !type->isFloatingPointTy())) {
791
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 mLog.error("variable is an unsupported type for "
792 "crement. Must be a non-boolean scalar", node);
793 8 return false;
794 }
795 else {
796 llvm::Value* crement = nullptr;
797
3/4
✓ Branch 0 taken 304 times.
✓ Branch 1 taken 462 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 304 times.
766 assert((node->increment() || node->decrement()) && "unrecognised crement operation");
798
2/2
✓ Branch 0 taken 462 times.
✓ Branch 1 taken 304 times.
766 if (node->increment()) crement = LLVMType<int32_t>::get(mContext, 1);
799
1/2
✓ Branch 0 taken 304 times.
✗ Branch 1 not taken.
304 else if (node->decrement()) crement = LLVMType<int32_t>::get(mContext, -1);
800
801 766 crement = arithmeticConversion(crement, type, mBuilder);
802
2/2
✓ Branch 0 taken 454 times.
✓ Branch 1 taken 312 times.
766 if (type->isIntegerTy()) crement = mBuilder.CreateAdd(rvalue, crement);
803 312 if (type->isFloatingPointTy()) crement = mBuilder.CreateFAdd(rvalue, crement);
804
805 766 mBuilder.CreateStore(crement, value);
806
807 // decide what to put on the expression stack
808 }
809 mValues.pop();
810
811
2/2
✓ Branch 0 taken 340 times.
✓ Branch 1 taken 426 times.
766 if (node->post()) mValues.push(rvalue);
812 else mValues.push(value);
813 return true;
814 }
815
816 4943 bool ComputeGenerator::visit(const ast::FunctionCall* node)
817 {
818 const FunctionGroup* const function =
819 4943 mFunctionRegistry.getOrInsert(node->name(), mOptions, false);
820
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4942 times.
4943 if (!function) {
821
2/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
2 mLog.error("unable to locate function \"" + node->name() + "\"", node);
822 1 return false;
823 }
824 else {
825 const size_t args = node->children();
826
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4942 times.
4942 assert(mValues.size() >= args);
827
828 // initialize arguments. scalars are always passed by value, arrays
829 // and strings always by pointer
830
831 std::vector<llvm::Value*> arguments;
832
1/2
✓ Branch 1 taken 4942 times.
✗ Branch 2 not taken.
4942 arguments.resize(args);
833
834
2/2
✓ Branch 0 taken 6944 times.
✓ Branch 1 taken 4942 times.
11886 for (auto r = arguments.rbegin(); r != arguments.rend(); ++r) {
835
1/2
✓ Branch 0 taken 6944 times.
✗ Branch 1 not taken.
6944 llvm::Value* arg = mValues.top(); mValues.pop();
836 llvm::Type* type = arg->getType();
837
2/2
✓ Branch 0 taken 3335 times.
✓ Branch 1 taken 3609 times.
6944 if (type->isPointerTy()) {
838 type = type->getPointerElementType();
839
2/2
✓ Branch 0 taken 3311 times.
✓ Branch 1 taken 24 times.
3335 if (type->isIntegerTy() || type->isFloatingPointTy()) {
840 // pass by value
841
3/4
✓ Branch 2 taken 404 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 6 times.
413 arg = mBuilder.CreateLoad(arg);
842 }
843 }
844 else {
845 // arrays should never be loaded
846
3/6
✓ Branch 0 taken 3609 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 3609 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 3609 times.
3609 assert(!type->isArrayTy() && type != LLVMType<codegen::String>::get(mContext));
847 if (type->isIntegerTy() || type->isFloatingPointTy()) {
848 /*pass by value*/
849 }
850 }
851 6944 *r = arg;
852 }
853
854 std::vector<llvm::Type*> inputTypes;
855
1/2
✓ Branch 1 taken 4942 times.
✗ Branch 2 not taken.
4942 valuesToTypes(arguments, inputTypes);
856
857 Function::SignatureMatch match;
858
1/2
✓ Branch 1 taken 4942 times.
✗ Branch 2 not taken.
4942 const Function* target = function->match(inputTypes, mContext, &match);
859
860
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4942 times.
4942 if (!target) {
861 assert(!function->list().empty()
862 && "FunctionGroup has no function declarations");
863
864 std::ostringstream os;
865 if (match == Function::SignatureMatch::None) {
866 os << "wrong number of arguments. \"" << node->name() << "\""
867 << " was called with: (";
868 llvm::raw_os_ostream stream(os);
869 printTypes(stream, inputTypes);
870 stream << ")";
871 }
872 else {
873 // match == Function::SignatureMatch::Size
874 os << "no matching function for ";
875 printSignature(os, inputTypes,
876 LLVMType<void>::get(mContext),
877 node->name().c_str(), {}, true);
878 }
879
880 os << " \ncandidates are: ";
881 for (const auto& sig : function->list()) {
882 os << std::endl;
883 sig->print(mContext, os, node->name().c_str());
884 }
885 mLog.error(os.str(), node);
886 return false;
887 }
888 else {
889 4942 llvm::Value* result = nullptr;
890
2/2
✓ Branch 0 taken 684 times.
✓ Branch 1 taken 4258 times.
4942 if (match == Function::SignatureMatch::Implicit) {
891
3/6
✓ Branch 1 taken 684 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 684 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 684 times.
1368 if (!mLog.warning("implicit conversion in function call", node)) return false;
892
1/2
✓ Branch 1 taken 684 times.
✗ Branch 2 not taken.
684 result = target->call(arguments, mBuilder, /*cast=*/true);
893 }
894 else {
895 // match == Function::SignatureMatch::Explicit
896
2/2
✓ Branch 1 taken 4249 times.
✓ Branch 2 taken 9 times.
4258 result = target->call(arguments, mBuilder, /*cast=*/false);
897 }
898
899
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4933 times.
4933 assert(result && "Function has been invoked with no valid llvm Value return");
900 mValues.push(result);
901 }
902 }
903 4933 return true;
904 }
905
906
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
82 bool ComputeGenerator::visit(const ast::Cast* node)
907 {
908
1/2
✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
82 llvm::Value* value = mValues.top(); mValues.pop();
909
910 llvm::Type* type =
911
2/2
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 2 times.
82 value->getType()->isPointerTy() ?
912 value->getType()->getPointerElementType() :
913 value->getType();
914
915
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 50 times.
82 if (!type->isIntegerTy() && !type->isFloatingPointTy()) {
916 mLog.error("unable to cast non scalar values", node);
917 return false;
918 }
919 else {
920 // If the value to cast is already the correct type, return
921 82 llvm::Type* targetType = llvmTypeFromToken(node->type(), mContext);
922
1/2
✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
82 if (type == targetType) return true;
923
924
925
2/2
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 2 times.
82 if (value->getType()->isPointerTy()) {
926 80 value = mBuilder.CreateLoad(value);
927 }
928
929
2/2
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 66 times.
82 if (targetType->isIntegerTy(1)) {
930 // if target is bool, perform standard boolean conversion (*not* truncation).
931 16 value = boolComparison(value, mBuilder);
932
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
16 assert(value->getType()->isIntegerTy(1));
933 }
934 else {
935 66 value = arithmeticConversion(value, targetType, mBuilder);
936 }
937 mValues.push(value);
938 }
939
940 return true;
941 }
942
943 3587 bool ComputeGenerator::visit(const ast::DeclareLocal* node)
944 {
945 // create storage for the local value.
946
2/2
✓ Branch 1 taken 135 times.
✓ Branch 2 taken 3452 times.
3587 llvm::Type* type = llvmTypeFromToken(node->type(), mContext);
947 llvm::Value* value;
948
949 // @note For strings, we call the string::string function rather than
950 // rely on the behaviour of insertStaticAlloca. The key difference here is
951 // that the string::string method performs the complete list of functions
952 // that are comprised by the ax::codegen::String constructor. In other
953 // words, it ensures all observable behaviour matches between the IR for
954 // strings and the C++ string implementation. Importantly,
955 // insertStaticAlloca does not initialise the first character of the SSO
956 // array to '\0' and does not call alloc (which, although does not change
957 // the string state compared to insertStaticAlloca, may change the order
958 // of assignments and other observable behaviour). Ideally,
959 // insertStaticAlloca should call string::string.
960
2/2
✓ Branch 0 taken 135 times.
✓ Branch 1 taken 3452 times.
3587 if (node->type() == ast::tokens::STRING) {
961
2/4
✓ Branch 1 taken 135 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 135 times.
✗ Branch 5 not taken.
135 const FunctionGroup* axstring = this->getFunction("string::string", /*internal*/true);
962
2/4
✓ Branch 1 taken 135 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 135 times.
135 value = axstring->execute({}, mBuilder);
963 }
964 else {
965 3452 value = insertStaticAlloca(mBuilder, type);
966 }
967
968
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3587 times.
3587 assert(value);
969 3587 SymbolTable* current = mSymbolTables.getOrInsert(mScopeIndex);
970
971 const std::string& name = node->local()->name();
972
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3584 times.
3587 if (!current->insert(name, value)) {
973
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
6 mLog.error("local variable \"" + name +
974 "\" has already been declared", node);
975 3 return false;
976 }
977
978
2/2
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 3552 times.
3584 if (mSymbolTables.find(name, mScopeIndex - 1)) {
979
2/4
✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 32 times.
✗ Branch 5 not taken.
96 if (!mLog.warning("declaration of variable \"" + name
980
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
64 + "\" shadows a previous declaration", node)) return false;
981 }
982
983
984 // do this to ensure all AST nodes are visited
985 // shouldn't ever fail
986
1/2
✓ Branch 0 taken 3584 times.
✗ Branch 1 not taken.
3584 if (this->traverse(node->local())) {
987
1/2
✓ Branch 0 taken 3584 times.
✗ Branch 1 not taken.
3584 value = mValues.top(); mValues.pop();
988 }
989 else if (mLog.atErrorLimit()) return false;
990
991
2/2
✓ Branch 0 taken 2858 times.
✓ Branch 1 taken 726 times.
3584 if (node->hasInit()) {
992
1/2
✓ Branch 1 taken 2858 times.
✗ Branch 2 not taken.
2858 if (this->traverse(node->init())) {
993
1/2
✓ Branch 0 taken 2858 times.
✗ Branch 1 not taken.
2858 llvm::Value* init = mValues.top(); mValues.pop();
994
2/2
✓ Branch 0 taken 2163 times.
✓ Branch 1 taken 695 times.
2858 llvm::Type* initType = init->getType();
995
996
2/2
✓ Branch 0 taken 2163 times.
✓ Branch 1 taken 695 times.
2858 if (initType->isPointerTy()) {
997 initType = initType->getPointerElementType();
998
1/2
✓ Branch 0 taken 2163 times.
✗ Branch 1 not taken.
2163 if (initType->isIntegerTy() || initType->isFloatingPointTy()) {
999 init = mBuilder.CreateLoad(init);
1000 }
1001 }
1002
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 2851 times.
2858 if (!this->assignExpression(value, init, node)) return false;
1003
1004 // note that loop conditions allow uses of initialized declarations
1005 // and so require the value
1006
1/2
✓ Branch 0 taken 2851 times.
✗ Branch 1 not taken.
2851 if (value) mValues.push(value);
1007 }
1008 else if (mLog.atErrorLimit()) return false;
1009 }
1010 return true;
1011 }
1012
1013 10147 bool ComputeGenerator::visit(const ast::Local* node)
1014 {
1015 // Reverse iterate through the current blocks and use the first declaration found
1016 // The current block number looks something like as follows
1017 //
1018 // ENTRY: Block 1
1019 //
1020 // if(...) // Block 3
1021 // {
1022 // if(...) {} // Block 5
1023 // }
1024 // else {} // Block 2
1025 //
1026 // Note that in block 5, block 2 variables will be queried. However block variables
1027 // are constructed from the top down, so although the block number is used for
1028 // reverse iterating, block 2 will not contain any information
1029 //
1030
1031 10147 llvm::Value* value = mSymbolTables.find(node->name());
1032
2/2
✓ Branch 0 taken 10126 times.
✓ Branch 1 taken 21 times.
10147 if (value) {
1033 mValues.push(value);
1034 }
1035 else {
1036
2/4
✓ Branch 2 taken 21 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 21 times.
✗ Branch 6 not taken.
42 mLog.error("variable \"" + node->name() + "\" hasn't been declared", node);
1037 21 return false;
1038 }
1039 return true;
1040 }
1041
1042
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2510 times.
2510 bool ComputeGenerator::visit(const ast::ArrayUnpack* node)
1043 {
1044
1/2
✓ Branch 0 taken 2510 times.
✗ Branch 1 not taken.
2510 llvm::Value* value = mValues.top(); mValues.pop();
1045
1/2
✓ Branch 0 taken 2510 times.
✗ Branch 1 not taken.
2510 llvm::Value* component0 = mValues.top(); mValues.pop();
1046 llvm::Value* component1 = nullptr;
1047
1048
2/2
✓ Branch 0 taken 419 times.
✓ Branch 1 taken 2091 times.
2510 if (node->isMatrixIndex()) {
1049
1/2
✓ Branch 0 taken 419 times.
✗ Branch 1 not taken.
419 component1 = mValues.top(); mValues.pop();
1050 // if double indexing, the two component values will be
1051 // pushed onto the stack with the first index last. i.e.
1052 // top: expression
1053 // 2nd index (if matrix access)
1054 // bottom: 1st index
1055 // so swap the components
1056 std::swap(component0, component1);
1057 }
1058
1059
1/2
✓ Branch 0 taken 2510 times.
✗ Branch 1 not taken.
2510 llvm::Type* type = value->getType();
1060
3/4
✓ Branch 0 taken 2510 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2509 times.
5020 if (!type->isPointerTy() ||
1061 !type->getPointerElementType()->isArrayTy()) {
1062
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 mLog.error("variable is not a valid type for component access", node);
1063 1 return false;
1064 }
1065
1066 // type now guaranteed to be an array type
1067 type = type->getPointerElementType();
1068 const size_t size = type->getArrayNumElements();
1069
4/4
✓ Branch 0 taken 419 times.
✓ Branch 1 taken 2090 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 418 times.
2509 if (component1 && size <= 4) {
1070 {
1071
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 mLog.error("attribute or local variable is not a compatible matrix type "
1072 "for [,] indexing", node);
1073 1 return false;
1074 }
1075 }
1076
1077
2/2
✓ Branch 0 taken 108 times.
✓ Branch 1 taken 2400 times.
2508 if (component0->getType()->isPointerTy()) {
1078 108 component0 = mBuilder.CreateLoad(component0);
1079 }
1080
4/4
✓ Branch 0 taken 418 times.
✓ Branch 1 taken 2090 times.
✓ Branch 2 taken 17 times.
✓ Branch 3 taken 401 times.
2508 if (component1 && component1->getType()->isPointerTy()) {
1081 17 component1 = mBuilder.CreateLoad(component1);
1082 }
1083
1084
4/4
✓ Branch 0 taken 2504 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 417 times.
✓ Branch 3 taken 2087 times.
2508 if (!component0->getType()->isIntegerTy() ||
1085
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 416 times.
417 (component1 && !component1->getType()->isIntegerTy())) {
1086 10 std::ostringstream os;
1087 5 llvm::raw_os_ostream stream(os);
1088
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 component0->getType()->print(stream);
1089
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if (component1) {
1090
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 stream << ", ";
1091
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 component1->getType()->print(stream);
1092 }
1093 stream.flush();
1094 {
1095
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 mLog.error("unable to index into array with a non integer value. Types are ["
1096
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 + os.str() + "]", node);
1097 return false;
1098 }
1099 }
1100
1101 2503 llvm::Value* zero = LLVMType<int32_t>::get(mContext, 0);
1102
2/2
✓ Branch 0 taken 2087 times.
✓ Branch 1 taken 416 times.
2503 if (!component1) {
1103 2087 value = mBuilder.CreateGEP(value, {zero, component0});
1104 }
1105 else {
1106 // component0 = row, component1 = column. Index into the matrix array
1107 // which is layed out in row major = (component0*dim + component1)
1108
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 416 times.
416 assert(size == 9 || size == 16);
1109
2/2
✓ Branch 0 taken 256 times.
✓ Branch 1 taken 160 times.
416 const int32_t dim = size == 9 ? 3 : 4;
1110 llvm::Value* offset =
1111 416 LLVMType<int32_t>::get(mContext, static_cast<int32_t>(dim));
1112 416 component0 = binaryOperator(component0, offset, ast::tokens::MULTIPLY, mBuilder);
1113 416 component0 = binaryOperator(component0, component1, ast::tokens::PLUS, mBuilder);
1114 416 value = mBuilder.CreateGEP(value, {zero, component0});
1115 }
1116
1117 mValues.push(value);
1118 return true;
1119 }
1120
1121
1/2
✓ Branch 0 taken 3939 times.
✗ Branch 1 not taken.
3939 bool ComputeGenerator::visit(const ast::ArrayPack* node)
1122 {
1123 const size_t num = node->children();
1124
1125 // if there is only one element on the stack, leave it as a pointer to a scalar
1126 // or another array
1127
1/2
✓ Branch 0 taken 3939 times.
✗ Branch 1 not taken.
3939 if (num == 1) return true;
1128
1129 3939 llvm::Type* strtype = LLVMType<codegen::String>::get(mContext);
1130
1131 std::vector<llvm::Value*> values;
1132
1/2
✓ Branch 1 taken 3939 times.
✗ Branch 2 not taken.
3939 values.reserve(num);
1133
2/2
✓ Branch 0 taken 21952 times.
✓ Branch 1 taken 3931 times.
25883 for (size_t i = 0; i < num; ++i) {
1134
1/2
✓ Branch 0 taken 21952 times.
✗ Branch 1 not taken.
21952 llvm::Value* value = mValues.top(); mValues.pop();
1135
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 21944 times.
21952 if (value->getType()->isPointerTy()) {
1136
1/4
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
8 value = mBuilder.CreateLoad(value);
1137 }
1138
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 21946 times.
21952 if (value->getType()->isArrayTy()) {
1139
3/6
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
12 mLog.error("cannot build nested arrays", node->child(num-(i+1)));
1140 8 return false;
1141 }
1142
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 21944 times.
21946 if (value->getType() == strtype) {
1143
3/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
4 mLog.error("cannot build arrays of strings", node->child(num-(i+1)));
1144 2 return false;
1145 }
1146
1147
1/2
✓ Branch 1 taken 21944 times.
✗ Branch 2 not taken.
21944 values.emplace_back(value);
1148 }
1149
1150 // reserve the values
1151 // @todo this should probably be handled by the AST
1152 3931 std::reverse(values.begin(), values.end());
1153
1154
2/4
✓ Branch 1 taken 3931 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3931 times.
✗ Branch 5 not taken.
3931 llvm::Value* array = arrayPackCast(values, mBuilder);
1155 mValues.push(array);
1156 return true;
1157 }
1158
1159 667 bool ComputeGenerator::visit(const ast::Value<bool>* node)
1160 {
1161 667 llvm::Constant* value = LLVMType<bool>::get(mContext, node->value());
1162 667 mValues.push(value);
1163 667 return true;
1164 }
1165
1166 bool ComputeGenerator::visit(const ast::Value<int16_t>* node)
1167 {
1168 return visit<int16_t>(node);
1169 }
1170
1171 8181 bool ComputeGenerator::visit(const ast::Value<int32_t>* node)
1172 {
1173 8181 return visit<int32_t>(node);
1174 }
1175
1176 396 bool ComputeGenerator::visit(const ast::Value<int64_t>* node)
1177 {
1178 396 return visit<int64_t>(node);
1179 }
1180
1181 11472 bool ComputeGenerator::visit(const ast::Value<float>* node)
1182 {
1183 11472 return visit<float>(node);
1184 }
1185
1186 11835 bool ComputeGenerator::visit(const ast::Value<double>* node)
1187 {
1188 11835 return visit<double>(node);
1189 }
1190
1191
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 605 times.
605 bool ComputeGenerator::visit(const ast::Value<std::string>* node)
1192 {
1193
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 605 times.
605 assert(node->value().size() < static_cast<size_t>(std::numeric_limits<size_t>::max()));
1194
2/4
✓ Branch 1 taken 605 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 605 times.
✗ Branch 5 not taken.
605 const FunctionGroup* axstring = this->getFunction("string::string", /*internal*/true);
1195 605 llvm::Value* loc = mBuilder.CreateGlobalStringPtr(node->value()); // char*
1196
3/6
✓ Branch 1 taken 605 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 605 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 605 times.
✗ Branch 7 not taken.
1210 llvm::Value* result = axstring->execute({loc}, mBuilder);
1197 mValues.push(result);
1198 605 return true;
1199 }
1200
1201 26599 const FunctionGroup* ComputeGenerator::getFunction(const std::string &identifier,
1202 const bool allowInternal)
1203 {
1204 const FunctionGroup* F =
1205 26599 mFunctionRegistry.getOrInsert(identifier, mOptions, allowInternal);
1206
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26599 times.
26599 assert(F);
1207 26599 return F;
1208 }
1209
1210 template <typename ValueType>
1211 typename std::enable_if<std::is_integral<ValueType>::value, bool>::type
1212
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8569 times.
17154 ComputeGenerator::visit(const ast::Value<ValueType>* node)
1213 {
1214 using ContainerT = typename ast::Value<ValueType>::ContainerType;
1215
1216 static const ContainerT max =
1217 static_cast<ContainerT>(std::numeric_limits<ValueType>::max());
1218
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8569 times.
17154 if (node->asContainerType() > max) {
1219
3/6
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
32 if (!mLog.warning("signed integer overflow in integer literal "
1220 + std::to_string(node->asContainerType()), node)) return false;
1221 }
1222
1223 17154 llvm::Constant* value = LLVMType<ValueType>::get(mContext, node->value());
1224 17154 mValues.push(value);
1225 17154 return true;
1226 }
1227
1228 template <typename ValueType>
1229 typename std::enable_if<std::is_floating_point<ValueType>::value, bool>::type
1230
1/2
✓ Branch 0 taken 23307 times.
✗ Branch 1 not taken.
46614 ComputeGenerator::visit(const ast::Value<ValueType>* node)
1231 {
1232
2/4
✓ Branch 0 taken 23307 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 23307 times.
46614 assert(std::isinf(node->value()) || node->value() >= 0.0);
1233 46614 llvm::Constant* value = LLVMType<ValueType>::get(mContext, node->value());
1234 46614 mValues.push(value);
1235 46614 return true;
1236 }
1237
1238 77 bool ComputeGenerator::visit(const ast::ExternalVariable* node)
1239 {
1240 const std::string globalName = node->tokenname();
1241 77 llvm::Value* ptrToAddress = this->globals().get(globalName);
1242
1243
1/2
✓ Branch 0 taken 77 times.
✗ Branch 1 not taken.
77 if (!ptrToAddress) {
1244 ptrToAddress = llvm::cast<llvm::GlobalVariable>
1245
3/6
✓ Branch 1 taken 77 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 77 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 77 times.
✗ Branch 9 not taken.
77 (mModule.getOrInsertGlobal(globalName, LLVMType<uintptr_t>::get(mContext)));
1246
1/2
✓ Branch 1 taken 77 times.
✗ Branch 2 not taken.
77 this->globals().insert(globalName, ptrToAddress);
1247 }
1248
1249
1/2
✓ Branch 1 taken 77 times.
✗ Branch 2 not taken.
77 llvm::Type* type = llvmTypeFromToken(node->type(), mContext);
1250
1/2
✓ Branch 2 taken 77 times.
✗ Branch 3 not taken.
77 llvm::Value* address = mBuilder.CreateLoad(ptrToAddress);
1251
3/4
✓ Branch 2 taken 77 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 65 times.
✓ Branch 5 taken 12 times.
154 llvm::Value* value = mBuilder.CreateIntToPtr(address, type->getPointerTo(0));
1252
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 12 times.
77 if (type->isIntegerTy() || type->isFloatingPointTy()) {
1253
1/2
✓ Branch 2 taken 21 times.
✗ Branch 3 not taken.
21 value = mBuilder.CreateLoad(value);
1254 }
1255 mValues.push(value);
1256 77 return true;
1257 }
1258
1259 1526 bool ComputeGenerator::visit(const ast::Tree*)
1260 {
1261 // In case we haven't returned already (i.e. we are NOT in a null block)
1262 // we insert a ret void. If we are, this will just get cleaned up anyway below.
1263 1526 mBuilder.CreateRetVoid();
1264
1/2
✓ Branch 0 taken 1526 times.
✗ Branch 1 not taken.
1526 mBuilder.SetInsertPoint(&mFunction->back());
1265 1526 return true;
1266 }
1267
1268 bool ComputeGenerator::visit(const ast::Attribute*)
1269 {
1270 assert(false && "Base ComputeGenerator attempted to generate code for an Attribute. "
1271 "PointComputeGenerator or VolumeComputeGenerator should be used for "
1272 "attribute accesses.");
1273 return false;
1274 }
1275
1276 14306 bool ComputeGenerator::assignExpression(llvm::Value* lhs, llvm::Value*& rhs, const ast::Node* node)
1277 {
1278 14306 llvm::Type* strtype = LLVMType<codegen::String>::get(mContext);
1279
1280 llvm::Type* ltype = lhs->getType();
1281
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14306 times.
14306 llvm::Type* rtype = rhs->getType();
1282
1283
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14306 times.
14306 if (!ltype->isPointerTy()) {
1284 mLog.error("unable to assign to an rvalue", node);
1285 return false;
1286 }
1287
1288 ltype = ltype->getPointerElementType();
1289
2/2
✓ Branch 0 taken 5701 times.
✓ Branch 1 taken 8605 times.
14306 if (rtype->isPointerTy()) rtype = rtype->getPointerElementType();
1290
1291
4/4
✓ Branch 0 taken 5772 times.
✓ Branch 1 taken 8534 times.
✓ Branch 2 taken 5400 times.
✓ Branch 3 taken 8906 times.
20078 size_t lsize = ltype->isArrayTy() ? ltype->getArrayNumElements() : 1;
1292
2/2
✓ Branch 0 taken 5400 times.
✓ Branch 1 taken 8906 times.
14306 size_t rsize = rtype->isArrayTy() ? rtype->getArrayNumElements() : 1;
1293
1294 // Handle scalar->matrix promotion if necessary
1295 // @todo promote all values (i.e. scalar to vectors) to make below branching
1296 // easier. Need to verifier IR is able to optimise to the same logic
1297
1298
2/2
✓ Branch 0 taken 1833 times.
✓ Branch 1 taken 12473 times.
14306 if (lsize == 9 || lsize == 16) {
1299
2/2
✓ Branch 0 taken 1781 times.
✓ Branch 1 taken 52 times.
1833 if (rtype->isIntegerTy() || rtype->isFloatingPointTy()) {
1300
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 100 times.
100 if (rhs->getType()->isPointerTy()) {
1301 rhs = mBuilder.CreateLoad(rhs);
1302 }
1303
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 100 times.
100 rhs = arithmeticConversion(rhs, ltype->getArrayElementType(), mBuilder);
1304
3/4
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 56 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 100 times.
144 rhs = scalarToMatrix(rhs, mBuilder, lsize == 9 ? 3 : 4);
1305 rtype = rhs->getType()->getPointerElementType();
1306 100 rsize = lsize;
1307 }
1308 }
1309
1310
2/2
✓ Branch 0 taken 318 times.
✓ Branch 1 taken 13988 times.
14306 if (lsize != rsize) {
1311
4/4
✓ Branch 0 taken 303 times.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 287 times.
318 if (lsize > 1 && rsize > 1) {
1312
2/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
16 mLog.error("unable to assign vector/array "
1313 "attributes with mismatching sizes", node);
1314 16 return false;
1315 }
1316
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 287 times.
302 else if (lsize == 1) {
1317
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 assert(rsize > 1);
1318
2/4
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
15 mLog.error("cannot assign a scalar value "
1319 "from a vector or matrix. Consider using the [] operator to "
1320 "get a particular element", node);
1321 15 return false;
1322 }
1323 }
1324
1325 // All remaining operators are either componentwise, string or invalid implicit casts
1326
1327 14275 const bool string =
1328 14275 (ltype == strtype && rtype == strtype);
1329
1330 const bool componentwise = !string &&
1331
6/6
✓ Branch 0 taken 13974 times.
✓ Branch 1 taken 301 times.
✓ Branch 2 taken 5470 times.
✓ Branch 3 taken 4174 times.
✓ Branch 4 taken 5469 times.
✓ Branch 5 taken 1 times.
23919 (rtype->isFloatingPointTy() || rtype->isIntegerTy() || rtype->isArrayTy()) &&
1332
4/4
✓ Branch 0 taken 5760 times.
✓ Branch 1 taken 3956 times.
✓ Branch 2 taken 5756 times.
✓ Branch 3 taken 4 times.
9716 (ltype->isFloatingPointTy() || ltype->isIntegerTy() || ltype->isArrayTy());
1333
1334 if (componentwise) {
1335
3/6
✓ Branch 0 taken 287 times.
✓ Branch 1 taken 13682 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 287 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
13969 assert(rsize == lsize || (rsize == 1 || lsize == 1));
1336 13969 const size_t resultsize = std::max(lsize, rsize);
1337
1338
2/2
✓ Branch 0 taken 1247 times.
✓ Branch 1 taken 12722 times.
13969 if (ltype != rtype) {
1339
2/2
✓ Branch 0 taken 709 times.
✓ Branch 1 taken 538 times.
1247 llvm::Type* letype = ltype->isArrayTy() ? ltype->getArrayElementType() : ltype;
1340
2/2
✓ Branch 0 taken 422 times.
✓ Branch 1 taken 825 times.
1247 llvm::Type* retype = rtype->isArrayTy() ? rtype->getArrayElementType() : rtype;
1341
2/2
✓ Branch 0 taken 1121 times.
✓ Branch 1 taken 126 times.
1247 if (letype != retype) {
1342 1121 llvm::Type* highest = typePrecedence(letype, retype);
1343
2/2
✓ Branch 0 taken 535 times.
✓ Branch 1 taken 586 times.
1121 if (highest != letype) {
1344
3/6
✓ Branch 1 taken 535 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 535 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 535 times.
✗ Branch 7 not taken.
1070 if (!mLog.warning("implicit conversion in assignment (possible truncation)", node)) return false;
1345 }
1346 }
1347 }
1348
1349 // compute the componentwise precision
1350
1351
2/2
✓ Branch 0 taken 5756 times.
✓ Branch 1 taken 8213 times.
13969 llvm::Type* opprec = ltype->isArrayTy() ? ltype->getArrayElementType() : ltype;
1352 // if target is bool, perform standard boolean conversion (*not* truncation).
1353 // i.e. if rhs is anything but zero, lhs is true
1354 // @todo zeroval should be at rhstype
1355
2/2
✓ Branch 1 taken 1843 times.
✓ Branch 2 taken 12126 times.
13969 if (opprec->isIntegerTy(1)) {
1356 1843 llvm::Value* newRhs = nullptr;
1357
1/2
✓ Branch 2 taken 1843 times.
✗ Branch 3 not taken.
1843 if (!this->binaryExpression(newRhs, LLVMType<int32_t>::get(mContext, 0), rhs, ast::tokens::NOTEQUALS, node)) return false;
1358
1/2
✓ Branch 0 taken 1843 times.
✗ Branch 1 not taken.
1843 if (!newRhs) return true;
1359 1843 rhs = newRhs;
1360
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1843 times.
1843 assert(newRhs->getType()->isIntegerTy(1));
1361 }
1362
1363
2/2
✓ Branch 0 taken 43071 times.
✓ Branch 1 taken 13969 times.
57040 for (size_t i = 0; i < resultsize; ++i) {
1364
2/2
✓ Branch 0 taken 34858 times.
✓ Branch 1 taken 8213 times.
43071 llvm::Value* lelement = lsize == 1 ? lhs : mBuilder.CreateConstGEP2_64(lhs, 0, i);
1365
4/6
✓ Branch 0 taken 9074 times.
✓ Branch 1 taken 33997 times.
✓ Branch 5 taken 33997 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 33997 times.
✗ Branch 9 not taken.
43071 llvm::Value* relement = rsize == 1 ? rhs : mBuilder.CreateLoad(mBuilder.CreateConstGEP2_64(rhs, 0, i));
1366 43071 relement = arithmeticConversion(relement, opprec, mBuilder);
1367 43071 mBuilder.CreateStore(relement, lelement);
1368 }
1369 }
1370
2/2
✓ Branch 0 taken 301 times.
✓ Branch 1 taken 5 times.
306 else if (string) {
1371
2/4
✓ Branch 1 taken 301 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 301 times.
✗ Branch 5 not taken.
301 const FunctionGroup* axstringassign = this->getFunction("string::op=", /*internal*/true);
1372
2/4
✓ Branch 1 taken 301 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 301 times.
✗ Branch 5 not taken.
602 axstringassign->execute({lhs, rhs}, mBuilder);
1373 }
1374 else {
1375
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
5 mLog.error("unsupported implicit cast in assignment", node);
1376 5 return false;
1377 }
1378 return true;
1379 }
1380
1381 ///////////////////////////////////////////////////////////////////////////
1382 ///////////////////////////////////////////////////////////////////////////
1383
1384
1385 3014 void ComputeGenerator::createFreeSymbolStrings(llvm::IRBuilder<>& B)
1386 {
1387 3014 llvm::Type* strtype = LLVMType<codegen::String>::get(mContext);
1388
1389 // Loop through the initial function allocations and create string clears
1390 // to any strings that were created. Allocs should only be made at the
1391 // start of the function, so we only have to scan the function entry block.
1392 //
1393 // @note technically, AX guarantees that the first set of instructions are
1394 // allocs, so we could stop on the first instr that isn't an alloc. This
1395 // would be hard to test though should this change in the future.
1396
1397 llvm::Function* F = B.GetInsertBlock()->getParent();
1398 llvm::BasicBlock& entry = F->getEntryBlock();
1399
1400 std::vector<llvm::Value*> ptrs;
1401
1402 // collect string allocas
1403
2/2
✓ Branch 0 taken 260804 times.
✓ Branch 1 taken 3014 times.
263818 for (auto& inst : entry) {
1404
2/2
✓ Branch 0 taken 224620 times.
✓ Branch 1 taken 36184 times.
295926 if (!llvm::isa<llvm::AllocaInst>(inst)) continue;
1405
2/2
✓ Branch 1 taken 35122 times.
✓ Branch 2 taken 1062 times.
36184 llvm::AllocaInst* alloc = llvm::cast<llvm::AllocaInst>(&inst);
1406
2/2
✓ Branch 0 taken 35122 times.
✓ Branch 1 taken 1062 times.
36184 if (alloc->getAllocatedType() != strtype) continue;
1407
1/2
✓ Branch 1 taken 1062 times.
✗ Branch 2 not taken.
1062 ptrs.emplace_back(alloc);
1408 }
1409
1410
2/2
✓ Branch 0 taken 2855 times.
✓ Branch 1 taken 159 times.
3014 if (ptrs.empty()) return;
1411
1412 // clear the strings to make sure malloc has been freed
1413 const FunctionGroup* axstringclear =
1414
2/4
✓ Branch 1 taken 159 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 159 times.
✗ Branch 5 not taken.
318 this->getFunction("string::clear", /*internal*/true);
1415
1416 159 const auto IP = B.saveIP();
1417
1418
2/2
✓ Branch 0 taken 2170 times.
✓ Branch 1 taken 159 times.
2329 for (llvm::BasicBlock& BB : *F) {
1419 llvm::Instruction* TI = BB.getTerminator();
1420
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2170 times.
2170 assert(TI);
1421
2/2
✓ Branch 0 taken 159 times.
✓ Branch 1 taken 2011 times.
2170 if (llvm::isa<llvm::ReturnInst>(TI)) {
1422
1/2
✓ Branch 1 taken 159 times.
✗ Branch 2 not taken.
159 B.SetInsertPoint(TI);
1423
2/2
✓ Branch 0 taken 1062 times.
✓ Branch 1 taken 159 times.
1221 for (auto ptr : ptrs) {
1424
2/6
✓ Branch 1 taken 1062 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1062 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
2124 axstringclear->execute({ptr}, B);
1425 }
1426 }
1427 }
1428
1429
1/2
✓ Branch 1 taken 159 times.
✗ Branch 2 not taken.
159 B.restoreIP(IP);
1430 }
1431
1432 5147 bool ComputeGenerator::binaryExpression(llvm::Value*& result, llvm::Value* lhs, llvm::Value* rhs,
1433 const ast::tokens::OperatorToken op, const ast::Node* node)
1434 {
1435 5147 llvm::Type* strtype = LLVMType<codegen::String>::get(mContext);
1436
1437 llvm::Type* ltype = lhs->getType();
1438 llvm::Type* rtype = rhs->getType();
1439
1440
2/2
✓ Branch 0 taken 2494 times.
✓ Branch 1 taken 2653 times.
5147 if (ltype->isPointerTy()) ltype = ltype->getPointerElementType();
1441
2/2
✓ Branch 0 taken 1536 times.
✓ Branch 1 taken 3611 times.
5147 if (rtype->isPointerTy()) rtype = rtype->getPointerElementType();
1442
1443
4/4
✓ Branch 0 taken 1273 times.
✓ Branch 1 taken 3874 times.
✓ Branch 2 taken 1270 times.
✓ Branch 3 taken 3877 times.
6420 size_t lsize = ltype->isArrayTy() ? ltype->getArrayNumElements() : 1;
1444
2/2
✓ Branch 0 taken 1270 times.
✓ Branch 1 taken 3877 times.
5147 size_t rsize = rtype->isArrayTy() ? rtype->getArrayNumElements() : 1;
1445
1446 // Handle scalar->matrix promotion if necessary
1447 // @todo promote all values (i.e. scalar to vectors) to make below branching
1448 // easier. Need to verifier IR is able to optimise to the same logic
1449
1450
2/2
✓ Branch 0 taken 322 times.
✓ Branch 1 taken 4825 times.
5147 if (lsize == 9 || lsize == 16) {
1451
2/2
✓ Branch 0 taken 320 times.
✓ Branch 1 taken 2 times.
322 if (rtype->isIntegerTy() || rtype->isFloatingPointTy()) {
1452
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (rhs->getType()->isPointerTy()) {
1453 rhs = mBuilder.CreateLoad(rhs);
1454 }
1455
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 rhs = arithmeticConversion(rhs, ltype->getArrayElementType(), mBuilder);
1456
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 rhs = scalarToMatrix(rhs, mBuilder, lsize == 9 ? 3 : 4);
1457 rtype = rhs->getType()->getPointerElementType();
1458 2 rsize = lsize;
1459
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (auto* child = node->child(0)) {
1460
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (child->isType<ast::ArrayPack>()) {
1461
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 mLog.error("unable to deduce implicit {...} type for binary op as value "
1462 "may be a matrix or array. assign to a local mat variable", child);
1463 2 return false;
1464 }
1465 }
1466 if (!mLog.warning("implicit cast to matrix from scalar. resulting "
1467 "cast will be equal to scalar * identity.", node->child(1))) return false;
1468 }
1469 }
1470
1471
2/2
✓ Branch 0 taken 321 times.
✓ Branch 1 taken 4824 times.
5145 if (rsize == 9 || rsize == 16) {
1472
1/2
✓ Branch 0 taken 321 times.
✗ Branch 1 not taken.
321 if (ltype->isIntegerTy() || ltype->isFloatingPointTy()) {
1473 if (lhs->getType()->isPointerTy()) {
1474 lhs = mBuilder.CreateLoad(lhs);
1475 }
1476 lhs = arithmeticConversion(lhs, rtype->getArrayElementType(), mBuilder);
1477 lhs = scalarToMatrix(lhs, mBuilder, rsize == 9 ? 3 : 4);
1478 ltype = lhs->getType()->getPointerElementType();
1479 lsize = rsize;
1480 if (auto* child = node->child(1)) {
1481 if (child->isType<ast::ArrayPack>()) {
1482 mLog.error("unable to deduce implicit {...} type for binary op as value "
1483 "may be a matrix or array. assign to a local mat variable", child);
1484 return false;
1485 }
1486 }
1487 if (!mLog.warning("implicit cast to matrix from scalar. resulting "
1488 "cast will be equal to scalar * identity.", node->child(0))) return false;
1489 }
1490 }
1491
1492 //
1493
1494 const ast::tokens::OperatorType opType = ast::tokens::operatorType(op);
1495 5145 result = nullptr;
1496 // Handle custom matrix operators
1497
1498
4/4
✓ Branch 0 taken 4825 times.
✓ Branch 1 taken 320 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4824 times.
5145 if (lsize >= 9 || rsize >= 9)
1499 {
1500
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 271 times.
321 if (op == ast::tokens::MULTIPLY) {
1501
6/6
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 25 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 24 times.
✓ Branch 4 taken 24 times.
✓ Branch 5 taken 2 times.
50 if ((lsize == 9 && rsize == 9) ||
1502
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 (lsize == 16 && rsize == 16)) {
1503 // matrix matrix multiplication all handled through mmmult
1504
5/10
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 48 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 48 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 48 times.
✗ Branch 13 not taken.
96 result = this->getFunction("mmmult", /*internal*/true)->execute({lhs, rhs}, mBuilder);
1505 }
1506
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 else if ((lsize == 9 && rsize == 3) ||
1507
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 (lsize == 16 && rsize == 3) ||
1508 (lsize == 16 && rsize == 4)) {
1509 // matrix vector multiplication all handled through pretransform
1510 result = this->getFunction("pretransform")->execute({lhs, rhs}, mBuilder);
1511 }
1512
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 else if ((lsize == 3 && rsize == 9) ||
1513
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 (lsize == 3 && rsize == 16) ||
1514 (lsize == 4 && rsize == 16)) {
1515 // vector matrix multiplication all handled through transform
1516 result = this->getFunction("transform")->execute({lhs, rhs}, mBuilder);
1517 }
1518 else {
1519
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 mLog.error("unsupported * operator on "
1520 "vector/matrix sizes", node);
1521 2 return false;
1522 }
1523 }
1524 271 else if (op == ast::tokens::MORETHAN ||
1525 op == ast::tokens::LESSTHAN ||
1526 271 op == ast::tokens::MORETHANOREQUAL ||
1527 271 op == ast::tokens::LESSTHANOREQUAL ||
1528
1/2
✓ Branch 0 taken 271 times.
✗ Branch 1 not taken.
271 op == ast::tokens::DIVIDE || // no / support for mats
1529 271 op == ast::tokens::MODULO || // no % support for mats
1530
4/4
✓ Branch 0 taken 268 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 256 times.
271 opType == ast::tokens::LOGICAL ||
1531 opType == ast::tokens::BITWISE) {
1532
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 mLog.error("call to unsupported operator \""
1533
2/4
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 15 times.
✗ Branch 6 not taken.
30 + ast::tokens::operatorNameFromToken(op) +
1534 "\" with a vector/matrix argument", node);
1535 15 return false;
1536 }
1537 }
1538
1539
2/2
✓ Branch 0 taken 5080 times.
✓ Branch 1 taken 48 times.
5128 if (!result) {
1540 // Handle matrix/vector ops of mismatching sizes
1541
3/4
✓ Branch 0 taken 3873 times.
✓ Branch 1 taken 1207 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3873 times.
5080 if (lsize > 1 || rsize > 1) {
1542
5/6
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1201 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 2 times.
1207 if (lsize != rsize && (lsize > 1 && rsize > 1)) {
1543
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 mLog.error("unsupported binary operator on vector/matrix "
1544 "arguments of mismatching sizes", node);
1545 4 return false;
1546 }
1547 1203 if (op == ast::tokens::MORETHAN ||
1548 op == ast::tokens::LESSTHAN ||
1549
2/2
✓ Branch 0 taken 1201 times.
✓ Branch 1 taken 2 times.
1203 op == ast::tokens::MORETHANOREQUAL ||
1550 op == ast::tokens::LESSTHANOREQUAL ||
1551 1201 opType == ast::tokens::LOGICAL ||
1552
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 1156 times.
1201 opType == ast::tokens::BITWISE) {
1553
1/2
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
47 mLog.error("call to unsupported operator \""
1554
2/4
✓ Branch 2 taken 47 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 47 times.
✗ Branch 6 not taken.
94 + ast::tokens::operatorNameFromToken(op) +
1555 "\" with a vector/matrix argument", node);
1556 47 return false;
1557 }
1558 }
1559
1560 // Handle invalid floating point ops
1561 if (rtype->isFloatingPointTy() || ltype->isFloatingPointTy()) {
1562
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 725 times.
735 if (opType == ast::tokens::BITWISE) {
1563
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 mLog.error("call to unsupported operator \""
1564
2/4
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 10 times.
✗ Branch 6 not taken.
20 + ast::tokens::operatorNameFromToken(op) +
1565 "\" with a floating point argument", node);
1566 10 return false;
1567 }
1568 }
1569 }
1570
1571 // All remaining operators are either componentwise, string or invalid implicit casts
1572
1573 const bool componentwise = !result &&
1574
6/6
✓ Branch 0 taken 5019 times.
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 1274 times.
✓ Branch 3 taken 3020 times.
✓ Branch 4 taken 1156 times.
✓ Branch 5 taken 118 times.
9361 (rtype->isFloatingPointTy() || rtype->isIntegerTy() || rtype->isArrayTy()) &&
1575
3/4
✓ Branch 0 taken 1156 times.
✓ Branch 1 taken 3028 times.
✓ Branch 2 taken 1156 times.
✗ Branch 3 not taken.
4184 (ltype->isFloatingPointTy() || ltype->isIntegerTy() || ltype->isArrayTy());
1576
1577 if (componentwise)
1578 {
1579
3/4
✓ Branch 0 taken 3745 times.
✓ Branch 1 taken 1156 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3028 times.
7929 assert(ltype->isArrayTy() || ltype->isFloatingPointTy() || ltype->isIntegerTy());
1580
3/4
✓ Branch 0 taken 3745 times.
✓ Branch 1 taken 1156 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3020 times.
7921 assert(rtype->isArrayTy() || rtype->isFloatingPointTy() || rtype->isIntegerTy());
1581
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 4901 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
4901 assert(rsize == lsize || (rsize == 1 || lsize == 1));
1582
1583
2/2
✓ Branch 0 taken 776 times.
✓ Branch 1 taken 4125 times.
4901 if (op == ast::tokens::DIVIDE || op == ast::tokens::MODULO) {
1584
2/2
✓ Branch 1 taken 560 times.
✓ Branch 2 taken 216 times.
776 if (llvm::Constant* c = llvm::dyn_cast<llvm::Constant>(rhs)) {
1585
2/2
✓ Branch 1 taken 288 times.
✓ Branch 2 taken 272 times.
560 if (c->isZeroValue()) {
1586
1/2
✓ Branch 0 taken 288 times.
✗ Branch 1 not taken.
288 if (op == ast::tokens::DIVIDE) {
1587
3/6
✓ Branch 1 taken 288 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 288 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 288 times.
✗ Branch 7 not taken.
576 if (!mLog.warning("division by zero is undefined", node)) return false;
1588 }
1589 else {
1590 if (!mLog.warning("modulo by zero is undefined", node)) return false;
1591 }
1592 }
1593 }
1594 }
1595
1596 // compute the componentwise precision
1597
1598
2/2
✓ Branch 0 taken 1156 times.
✓ Branch 1 taken 3745 times.
4901 llvm::Type* opprec = ltype->isArrayTy() ? ltype->getArrayElementType() : ltype;
1599
2/2
✓ Branch 0 taken 1156 times.
✓ Branch 1 taken 3745 times.
4901 opprec = rtype->isArrayTy() ?
1600 1156 typePrecedence(opprec, rtype->getArrayElementType()) :
1601 3745 typePrecedence(opprec, rtype);
1602
1603 // if bool, the lowest precision and subsequent result should be int32
1604 // for arithmetic, bitwise and certain other ops
1605 // @note - no bool containers, so if the type is a container, it can't
1606 // contain booleans
1607
2/2
✓ Branch 1 taken 216 times.
✓ Branch 2 taken 4685 times.
4901 if (opprec->isIntegerTy(1)) {
1608 216 if (opType == ast::tokens::ARITHMETIC ||
1609
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 152 times.
216 opType == ast::tokens::BITWISE ||
1610
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 24 times.
64 op == ast::tokens::MORETHAN ||
1611 op == ast::tokens::LESSTHAN ||
1612
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 16 times.
40 op == ast::tokens::MORETHANOREQUAL ||
1613 op == ast::tokens::LESSTHANOREQUAL) {
1614 200 opprec = LLVMType<int32_t>::get(mContext);
1615 }
1616 }
1617
1618 // load scalars once
1619
1620
2/2
✓ Branch 0 taken 3745 times.
✓ Branch 1 taken 1156 times.
4901 if (!ltype->isArrayTy()) {
1621
2/2
✓ Branch 0 taken 1092 times.
✓ Branch 1 taken 2653 times.
3745 if (lhs->getType()->isPointerTy()) {
1622 1092 lhs = mBuilder.CreateLoad(lhs);
1623 }
1624 }
1625
2/2
✓ Branch 0 taken 3745 times.
✓ Branch 1 taken 1156 times.
4901 if (!rtype->isArrayTy()) {
1626
2/2
✓ Branch 0 taken 148 times.
✓ Branch 1 taken 3597 times.
3745 if (rhs->getType()->isPointerTy()) {
1627 148 rhs = mBuilder.CreateLoad(rhs);
1628 }
1629 }
1630
1631
1/2
✓ Branch 1 taken 4901 times.
✗ Branch 2 not taken.
4901 const size_t resultsize = std::max(lsize, rsize);
1632 std::vector<llvm::Value*> elements;
1633
1/2
✓ Branch 1 taken 4901 times.
✗ Branch 2 not taken.
4901 elements.reserve(resultsize);
1634
1635 // handle floored modulo
1636 4901 const Function* target = nullptr;
1637 19290 auto runop = [&target, op, this](llvm::Value* a, llvm::Value* b) {
1638
4/6
✓ Branch 0 taken 532 times.
✓ Branch 1 taken 9113 times.
✓ Branch 3 taken 532 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 532 times.
✗ Branch 7 not taken.
10177 if (target) return target->call({a,b}, this->mBuilder, /*cast=*/false);
1639 9113 else return binaryOperator(a, b, op, this->mBuilder);
1640 4901 };
1641
1642
2/2
✓ Branch 0 taken 316 times.
✓ Branch 1 taken 4585 times.
4901 if (op == ast::tokens::MODULO) {
1643
2/4
✓ Branch 1 taken 316 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 316 times.
✗ Branch 5 not taken.
316 const FunctionGroup* mod = this->getFunction("floormod");
1644
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 316 times.
316 assert(mod);
1645
3/6
✓ Branch 1 taken 316 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 316 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 316 times.
✗ Branch 7 not taken.
316 target = mod->match({opprec,opprec}, mContext);
1646
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 316 times.
316 assert(target);
1647 }
1648
1649 // perform op
1650
2/2
✓ Branch 0 taken 9645 times.
✓ Branch 1 taken 4901 times.
14546 for (size_t i = 0; i < resultsize; ++i) {
1651
3/4
✓ Branch 0 taken 5900 times.
✓ Branch 1 taken 3745 times.
✓ Branch 5 taken 5900 times.
✗ Branch 6 not taken.
15545 llvm::Value* lelement = lsize == 1 ? lhs : mBuilder.CreateLoad(mBuilder.CreateConstGEP2_64(lhs, 0, i));
1652
3/4
✓ Branch 0 taken 5900 times.
✓ Branch 1 taken 3745 times.
✓ Branch 5 taken 5900 times.
✗ Branch 6 not taken.
15545 llvm::Value* relement = rsize == 1 ? rhs : mBuilder.CreateLoad(mBuilder.CreateConstGEP2_64(rhs, 0, i));
1653
1/2
✓ Branch 1 taken 9645 times.
✗ Branch 2 not taken.
9645 lelement = arithmeticConversion(lelement, opprec, mBuilder);
1654
1/2
✓ Branch 1 taken 9645 times.
✗ Branch 2 not taken.
9645 relement = arithmeticConversion(relement, opprec, mBuilder);
1655
2/4
✓ Branch 1 taken 9645 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9645 times.
✗ Branch 5 not taken.
9645 elements.emplace_back(runop(lelement, relement));
1656 }
1657
1658 // handle vec/mat results
1659
2/2
✓ Branch 0 taken 1156 times.
✓ Branch 1 taken 3745 times.
4901 if (resultsize > 1) {
1660
2/2
✓ Branch 0 taken 208 times.
✓ Branch 1 taken 948 times.
1156 if (op == ast::tokens::EQUALSEQUALS || op == ast::tokens::NOTEQUALS) {
1661
1/2
✓ Branch 1 taken 208 times.
✗ Branch 2 not taken.
208 const ast::tokens::OperatorToken reductionOp =
1662
2/2
✓ Branch 0 taken 104 times.
✓ Branch 1 taken 104 times.
208 op == ast::tokens::EQUALSEQUALS ? ast::tokens::AND : ast::tokens::OR;
1663
1/2
✓ Branch 1 taken 208 times.
✗ Branch 2 not taken.
208 result = elements.front();
1664
2/4
✓ Branch 1 taken 208 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 208 times.
208 assert(result->getType() == LLVMType<bool>::get(mContext));
1665
2/2
✓ Branch 0 taken 1024 times.
✓ Branch 1 taken 208 times.
1232 for (size_t i = 1; i < resultsize; ++i) {
1666
1/2
✓ Branch 1 taken 1024 times.
✗ Branch 2 not taken.
1024 result = binaryOperator(result, elements[i], reductionOp, mBuilder);
1667 }
1668 }
1669 else {
1670 // Create the allocation at the start of the function block
1671
1/2
✓ Branch 1 taken 948 times.
✗ Branch 2 not taken.
948 result = insertStaticAlloca(mBuilder,
1672
1/2
✓ Branch 1 taken 948 times.
✗ Branch 2 not taken.
948 llvm::ArrayType::get(opprec, resultsize));
1673
2/2
✓ Branch 0 taken 4668 times.
✓ Branch 1 taken 948 times.
5616 for (size_t i = 0; i < resultsize; ++i) {
1674
2/6
✓ Branch 2 taken 4668 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4668 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
4668 mBuilder.CreateStore(elements[i], mBuilder.CreateConstGEP2_64(result, 0, i));
1675 }
1676 }
1677 }
1678 else {
1679 3745 result = elements.front();
1680 }
1681 }
1682
1683
2/2
✓ Branch 0 taken 118 times.
✓ Branch 1 taken 4949 times.
5067 const bool string = !result &&
1684
2/2
✓ Branch 0 taken 117 times.
✓ Branch 1 taken 1 times.
118 (ltype == strtype && rtype == strtype);
1685
1686 if (string)
1687 {
1688
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 108 times.
117 if (op != ast::tokens::PLUS) {
1689
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 mLog.error("unsupported string operation \""
1690
2/4
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9 times.
✗ Branch 6 not taken.
18 + ast::tokens::operatorNameFromToken(op) + "\"", node);
1691 9 return false;
1692 }
1693
1694
2/4
✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 108 times.
✗ Branch 5 not taken.
108 const FunctionGroup* axstringplus = this->getFunction("string::op+", /*internal*/true);
1695
3/6
✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 108 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 108 times.
✗ Branch 7 not taken.
216 result = axstringplus->execute({lhs, rhs}, mBuilder);
1696 }
1697
1698
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5057 times.
5058 if (!result) {
1699
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 mLog.error("unsupported implicit cast in binary op", node);
1700 1 return false;
1701 }
1702
1703 return true;
1704 }
1705
1706 } // namespace codegen_internal
1707
1708 } // namespace codegen
1709 } // namespace ax
1710 } // namespace OPENVDB_VERSION_NAME
1711 } // namespace openvdb
1712
1713