src/irgenerator/IRGenerator.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright (c) 2021-2026 ChilliBits. All rights reserved. | ||
| 2 | |||
| 3 | #include "IRGenerator.h" | ||
| 4 | |||
| 5 | #include <SourceFile.h> | ||
| 6 | #include <driver/Driver.h> | ||
| 7 | #include <global/GlobalResourceManager.h> | ||
| 8 | #include <model/Function.h> | ||
| 9 | #include <symboltablebuilder/ScopeHandle.h> | ||
| 10 | #include <symboltablebuilder/SymbolTableBuilder.h> | ||
| 11 | #include <typechecker/FunctionManager.h> | ||
| 12 | |||
| 13 | #include <llvm/IR/Module.h> | ||
| 14 | #include <llvm/IR/Verifier.h> | ||
| 15 | |||
| 16 | namespace spice::compiler { | ||
| 17 | |||
| 18 | const std::string PRODUCER_STRING = "spice version " + std::string(SPICE_VERSION) + " (https://github.com/spicelang/spice)"; | ||
| 19 | |||
| 20 | 4583 | IRGenerator::IRGenerator(GlobalResourceManager &resourceManager, SourceFile *sourceFile) | |
| 21 | 4583 | : CompilerPass(resourceManager, sourceFile), context(cliOptions.useLTO ? resourceManager.ltoContext : sourceFile->context), | |
| 22 |
1/2✓ Branch 8 → 9 taken 4583 times.
✗ Branch 8 → 83 not taken.
|
4583 | builder(sourceFile->builder), module(sourceFile->llvmModule.get()), conversionManager(sourceFile, this), |
| 23 |
6/10✓ Branch 4 → 5 taken 4 times.
✓ Branch 4 → 6 taken 4579 times.
✓ Branch 9 → 10 taken 4583 times.
✗ Branch 9 → 83 not taken.
✓ Branch 10 → 11 taken 4583 times.
✗ Branch 10 → 83 not taken.
✓ Branch 11 → 12 taken 4583 times.
✗ Branch 11 → 81 not taken.
✓ Branch 15 → 16 taken 4583 times.
✗ Branch 15 → 77 not taken.
|
9166 | stdFunctionManager(sourceFile, resourceManager, module) { |
| 24 | // Attach information to the module | ||
| 25 |
1/2✓ Branch 19 → 20 taken 4583 times.
✗ Branch 19 → 56 not taken.
|
4583 | module->setTargetTriple(cliOptions.targetTriple); |
| 26 |
2/4✓ Branch 23 → 24 taken 4583 times.
✗ Branch 23 → 59 not taken.
✓ Branch 24 → 25 taken 4583 times.
✗ Branch 24 → 57 not taken.
|
4583 | module->setDataLayout(sourceFile->targetMachine->createDataLayout()); |
| 27 |
2/2✓ Branch 26 → 27 taken 2 times.
✓ Branch 26 → 29 taken 4581 times.
|
4583 | if (cliOptions.outputContainer == OutputContainer::SHARED_LIBRARY) { |
| 28 |
1/2✓ Branch 27 → 28 taken 2 times.
✗ Branch 27 → 69 not taken.
|
2 | module->setPICLevel(llvm::PICLevel::SmallPIC); |
| 29 |
1/2✓ Branch 28 → 31 taken 2 times.
✗ Branch 28 → 69 not taken.
|
2 | module->setPIELevel(llvm::PIELevel::Default); |
| 30 | } else { | ||
| 31 |
1/2✓ Branch 29 → 30 taken 4581 times.
✗ Branch 29 → 69 not taken.
|
4581 | module->setPICLevel(llvm::PICLevel::BigPIC); |
| 32 |
1/2✓ Branch 30 → 31 taken 4581 times.
✗ Branch 30 → 69 not taken.
|
4581 | module->setPIELevel(llvm::PIELevel::Large); |
| 33 | } | ||
| 34 |
1/2✓ Branch 31 → 32 taken 4583 times.
✗ Branch 31 → 69 not taken.
|
4583 | module->setUwtable(llvm::UWTableKind::Default); |
| 35 |
3/4✓ Branch 32 → 33 taken 2 times.
✓ Branch 32 → 34 taken 4581 times.
✓ Branch 35 → 36 taken 4583 times.
✗ Branch 35 → 69 not taken.
|
4583 | module->setFramePointer(cliOptions.keepFramePointers ? llvm::FramePointerKind::All : llvm::FramePointerKind::None); |
| 36 | |||
| 37 | // Add module identifier metadata | ||
| 38 |
2/4✓ Branch 36 → 37 taken 4583 times.
✗ Branch 36 → 60 not taken.
✓ Branch 37 → 38 taken 4583 times.
✗ Branch 37 → 60 not taken.
|
4583 | llvm::NamedMDNode *identifierMetadata = module->getOrInsertNamedMetadata("llvm.ident"); |
| 39 |
3/6✓ Branch 39 → 40 taken 4583 times.
✗ Branch 39 → 61 not taken.
✓ Branch 41 → 42 taken 4583 times.
✗ Branch 41 → 61 not taken.
✓ Branch 42 → 43 taken 4583 times.
✗ Branch 42 → 61 not taken.
|
4583 | identifierMetadata->addOperand(llvm::MDNode::get(context, llvm::MDString::get(context, PRODUCER_STRING))); |
| 40 | |||
| 41 | // Initialize common LLVM types | ||
| 42 |
4/8✓ Branch 43 → 44 taken 4583 times.
✗ Branch 43 → 64 not taken.
✓ Branch 44 → 45 taken 4583 times.
✗ Branch 44 → 64 not taken.
✓ Branch 45 → 46 taken 4583 times.
✗ Branch 45 → 64 not taken.
✓ Branch 47 → 48 taken 4583 times.
✗ Branch 47 → 64 not taken.
|
4583 | llvmTypes.lambdaFatPtrType = llvm::StructType::get(context, {builder.getPtrTy(), builder.getPtrTy(), builder.getInt64Ty()}); |
| 43 | |||
| 44 | // Initialize debug info generator | ||
| 45 |
2/2✓ Branch 50 → 51 taken 2310 times.
✓ Branch 50 → 55 taken 2273 times.
|
9166 | if (cliOptions.instrumentation.emitsDebugInfo()) |
| 46 |
2/4✓ Branch 51 → 52 taken 2310 times.
✗ Branch 51 → 68 not taken.
✓ Branch 52 → 53 taken 2310 times.
✗ Branch 52 → 66 not taken.
|
2310 | diGenerator.initialize(sourceFile->fileName, sourceFile->fileDir); |
| 47 | 4583 | } | |
| 48 | |||
| 49 | 4583 | std::any IRGenerator::visitEntry(const EntryNode *node) { | |
| 50 | // Generate IR | ||
| 51 |
1/2✓ Branch 2 → 3 taken 4583 times.
✗ Branch 2 → 28 not taken.
|
4583 | visitChildren(node); |
| 52 | |||
| 53 | // Generate test main if required | ||
| 54 |
4/4✓ Branch 4 → 5 taken 946 times.
✓ Branch 4 → 7 taken 3637 times.
✓ Branch 5 → 6 taken 10 times.
✓ Branch 5 → 7 taken 936 times.
|
4583 | if (sourceFile->isMainFile && cliOptions.generateTestMain) |
| 55 | 10 | generateTestMain(); | |
| 56 | |||
| 57 | // Execute deferred VTable initializations | ||
| 58 |
2/2✓ Branch 21 → 9 taken 2211 times.
✓ Branch 21 → 22 taken 4583 times.
|
11377 | for (DeferredLogic &deferredVTableInit : deferredVTableInitializations) |
| 59 |
1/2✓ Branch 11 → 12 taken 2211 times.
✗ Branch 11 → 29 not taken.
|
2211 | deferredVTableInit.execute(); |
| 60 | |||
| 61 | // Finalize debug info generator | ||
| 62 | 4583 | diGenerator.finalize(); | |
| 63 | |||
| 64 | // Verify module | ||
| 65 | 4583 | verifyModule(node->codeLoc); | |
| 66 | |||
| 67 |
1/2✓ Branch 24 → 25 taken 4583 times.
✗ Branch 24 → 30 not taken.
|
9166 | return nullptr; |
| 68 | } | ||
| 69 | |||
| 70 | 172113 | llvm::AllocaInst *IRGenerator::insertAlloca(llvm::Type *llvmType, const std::string &varName) { | |
| 71 |
2/2✓ Branch 2 → 3 taken 112587 times.
✓ Branch 2 → 8 taken 59526 times.
|
172113 | if (allocaInsertInst != nullptr) { // If there is already an alloca inst, insert right after that |
| 72 |
2/4✓ Branch 3 → 4 taken 112587 times.
✗ Branch 3 → 19 not taken.
✓ Branch 4 → 5 taken 112587 times.
✗ Branch 4 → 19 not taken.
|
112587 | llvm::AllocaInst *allocaInst = builder.CreateAlloca(llvmType, nullptr, varName); |
| 73 | 112587 | allocaInst->dropLocation(); // Part of prologue | |
| 74 | 112587 | allocaInst->moveAfter(allocaInsertInst); | |
| 75 | 112587 | allocaInsertInst = allocaInst; | |
| 76 | } else { // This is the first alloca inst in the current function -> insert at the entry block | ||
| 77 | // Save current basic block and move insert cursor to entry block of the current function | ||
| 78 | 59526 | llvm::BasicBlock *currentBlock = builder.GetInsertBlock(); | |
| 79 | 59526 | builder.SetInsertPoint(allocaInsertBlock, allocaInsertBlock->begin()); | |
| 80 | |||
| 81 | // Allocate the size of the given LLVM type | ||
| 82 |
2/4✓ Branch 11 → 12 taken 59526 times.
✗ Branch 11 → 20 not taken.
✓ Branch 12 → 13 taken 59526 times.
✗ Branch 12 → 20 not taken.
|
59526 | allocaInsertInst = builder.CreateAlloca(llvmType, nullptr, varName); |
| 83 | 59526 | allocaInsertInst->dropLocation(); // Part of prologue | |
| 84 | |||
| 85 | // Restore old basic block | ||
| 86 | 59526 | builder.SetInsertPoint(currentBlock); | |
| 87 | } | ||
| 88 | |||
| 89 | // Insert lifetime start marker | ||
| 90 |
2/2✓ Branch 15 → 16 taken 2342 times.
✓ Branch 15 → 17 taken 169771 times.
|
172113 | if (cliOptions.useLifetimeMarkers) |
| 91 | 2342 | builder.CreateLifetimeStart(allocaInsertInst); | |
| 92 | |||
| 93 | 172113 | return allocaInsertInst; | |
| 94 | } | ||
| 95 | |||
| 96 | 140507 | llvm::AllocaInst *IRGenerator::insertAlloca(const QualType &qualType, const std::string &varName) { | |
| 97 | 140507 | llvm::Type *llvmType = qualType.toLLVMType(sourceFile); | |
| 98 | 140507 | llvm::AllocaInst *alloca = insertAlloca(llvmType, varName); | |
| 99 | |||
| 100 | // Insert type metadata | ||
| 101 |
2/2✓ Branch 4 → 5 taken 8 times.
✓ Branch 4 → 6 taken 140499 times.
|
140507 | if (cliOptions.useTBAAMetadata) |
| 102 | 8 | mdGenerator.generateTypeMetadata(allocaInsertInst, qualType); | |
| 103 | |||
| 104 | 140507 | return alloca; | |
| 105 | } | ||
| 106 | |||
| 107 | 447930 | llvm::LoadInst *IRGenerator::insertLoad(llvm::Type *llvmType, llvm::Value *ptr, bool isVolatile, | |
| 108 | const std::string &varName) const { | ||
| 109 |
1/2✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 447930 times.
|
447930 | assert(ptr->getType()->isPointerTy()); |
| 110 |
2/4✓ Branch 6 → 7 taken 447930 times.
✗ Branch 6 → 11 not taken.
✓ Branch 7 → 8 taken 447930 times.
✗ Branch 7 → 11 not taken.
|
447930 | return builder.CreateLoad(llvmType, ptr, isVolatile, varName); |
| 111 | } | ||
| 112 | |||
| 113 | 388416 | llvm::LoadInst *IRGenerator::insertLoad(const QualType &qualType, llvm::Value *ptr, bool isVolatile, const std::string &varName) { | |
| 114 | 388416 | llvm::Type *llvmType = qualType.toLLVMType(sourceFile); | |
| 115 | 388416 | llvm::LoadInst *load = insertLoad(llvmType, ptr, isVolatile, varName); | |
| 116 |
2/2✓ Branch 4 → 5 taken 10 times.
✓ Branch 4 → 6 taken 388406 times.
|
388416 | if (cliOptions.useTBAAMetadata) |
| 117 | 10 | mdGenerator.generateTBAAMetadata(load, qualType); | |
| 118 | 388416 | return load; | |
| 119 | } | ||
| 120 | |||
| 121 | 225297 | llvm::StoreInst *IRGenerator::insertStore(llvm::Value *val, llvm::Value *ptr, bool isVolatile) const { | |
| 122 |
1/2✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 225297 times.
|
225297 | assert(ptr->getType()->isPointerTy()); |
| 123 | 225297 | return builder.CreateStore(val, ptr, isVolatile); | |
| 124 | } | ||
| 125 | |||
| 126 | 68733 | void IRGenerator::insertStore(llvm::Value *val, llvm::Value *ptr, const QualType &qualType, bool isVolatile) { | |
| 127 | 68733 | llvm::StoreInst *store = insertStore(val, ptr, isVolatile); | |
| 128 |
2/2✓ Branch 3 → 4 taken 8 times.
✓ Branch 3 → 5 taken 68725 times.
|
68733 | if (cliOptions.useTBAAMetadata) |
| 129 | 8 | mdGenerator.generateTBAAMetadata(store, qualType); | |
| 130 | 68733 | } | |
| 131 | |||
| 132 | 123923 | llvm::Value *IRGenerator::insertInBoundsGEP(llvm::Type *type, llvm::Value *basePtr, llvm::ArrayRef<llvm::Value *> indices, | |
| 133 | const std::string &varName) const { | ||
| 134 |
1/2✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 123923 times.
|
123923 | assert(basePtr->getType()->isPointerTy()); |
| 135 |
1/2✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 123923 times.
|
123923 | assert(!indices.empty()); |
| 136 |
4/6✓ Branch 4 → 5 taken 122287 times.
✓ Branch 4 → 7 taken 114459 times.
✓ Branch 6 → 7 taken 122287 times.
✗ Branch 6 → 8 not taken.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 123923 times.
|
360669 | assert(std::ranges::all_of(indices, [](const llvm::Value *index) { |
| 137 | const llvm::Type *indexType = index->getType(); | ||
| 138 | return indexType->isIntegerTy(32) || indexType->isIntegerTy(64); | ||
| 139 | })); | ||
| 140 | |||
| 141 | // Insert GEP | ||
| 142 |
2/4✓ Branch 12 → 13 taken 123923 times.
✗ Branch 12 → 17 not taken.
✓ Branch 13 → 14 taken 123923 times.
✗ Branch 13 → 17 not taken.
|
123923 | return builder.CreateInBoundsGEP(type, basePtr, indices, varName); |
| 143 | } | ||
| 144 | |||
| 145 | 36990 | llvm::Value *IRGenerator::insertStructGEP(llvm::Type *type, llvm::Value *basePtr, unsigned int index, | |
| 146 | const std::string &varName) const { | ||
| 147 |
1/2✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 36990 times.
|
36990 | assert(basePtr->getType()->isPointerTy()); |
| 148 | |||
| 149 | // If we use index 0 we can use the base pointer directly | ||
| 150 |
2/2✓ Branch 6 → 7 taken 10840 times.
✓ Branch 6 → 8 taken 26150 times.
|
36990 | if (index == 0) |
| 151 | 10840 | return basePtr; | |
| 152 | |||
| 153 | // Insert GEP | ||
| 154 |
2/4✓ Branch 8 → 9 taken 26150 times.
✗ Branch 8 → 13 not taken.
✓ Branch 9 → 10 taken 26150 times.
✗ Branch 9 → 13 not taken.
|
26150 | return builder.CreateStructGEP(type, basePtr, index, varName); |
| 155 | } | ||
| 156 | |||
| 157 | 254081 | llvm::Value *IRGenerator::resolveValue(const ExprNode *node) { | |
| 158 | // Visit the given AST node | ||
| 159 |
2/4✓ Branch 2 → 3 taken 254081 times.
✗ Branch 2 → 11 not taken.
✓ Branch 3 → 4 taken 254081 times.
✗ Branch 3 → 9 not taken.
|
254081 | auto exprResult = any_cast<LLVMExprResult>(visit(node)); |
| 160 |
1/2✓ Branch 5 → 6 taken 254081 times.
✗ Branch 5 → 12 not taken.
|
508162 | return resolveValue(node, exprResult); |
| 161 | } | ||
| 162 | |||
| 163 | /** | ||
| 164 | * Resolve the value of an expression in its expression scope, if it has one, and destruct the temporaries of the expression right | ||
| 165 | * afterwards. The value is already loaded at that point, so it does not depend on the temporaries anymore. | ||
| 166 | * | ||
| 167 | * @param expr Expression to resolve the value of | ||
| 168 | * @return Value of the expression | ||
| 169 | */ | ||
| 170 | 53364 | llvm::Value *IRGenerator::resolveValueInExprScope(const ExprNode *expr) { | |
| 171 |
1/2✓ Branch 2 → 3 taken 53364 times.
✗ Branch 2 → 12 not taken.
|
53364 | const ExprScopeHandle exprScopeHandle(this, expr); |
| 172 |
1/2✓ Branch 3 → 4 taken 53364 times.
✗ Branch 3 → 10 not taken.
|
53364 | llvm::Value *value = resolveValue(expr); |
| 173 |
2/2✓ Branch 5 → 6 taken 53356 times.
✓ Branch 5 → 7 taken 8 times.
|
53364 | if (const Scope *exprScope = exprScopeHandle.getExprScope()) |
| 174 |
1/2✓ Branch 6 → 7 taken 53356 times.
✗ Branch 6 → 10 not taken.
|
53356 | generateTemporariesCleanup(exprScope, expr); |
| 175 | 53364 | return value; | |
| 176 | 53364 | } | |
| 177 | |||
| 178 | 283829 | llvm::Value *IRGenerator::resolveValue(const ExprNode *node, LLVMExprResult &exprResult) { | |
| 179 | 283829 | return resolveValue(node->getEvaluatedSymbolType(manIdx), exprResult); | |
| 180 | } | ||
| 181 | |||
| 182 | 564281 | llvm::Value *IRGenerator::resolveValue(const QualType &qualType, LLVMExprResult &exprResult) { | |
| 183 | // Check if the value is already present | ||
| 184 |
2/2✓ Branch 2 → 3 taken 176263 times.
✓ Branch 2 → 4 taken 388018 times.
|
564281 | if (exprResult.value != nullptr) |
| 185 | 176263 | return exprResult.value; | |
| 186 | |||
| 187 | // Check if a constant is present | ||
| 188 |
2/2✓ Branch 4 → 5 taken 116588 times.
✓ Branch 4 → 7 taken 271430 times.
|
388018 | if (exprResult.constant != nullptr) { |
| 189 | 116588 | materializeConstant(exprResult); | |
| 190 | 116588 | return exprResult.value; | |
| 191 | } | ||
| 192 | |||
| 193 |
3/4✓ Branch 7 → 8 taken 3583 times.
✓ Branch 7 → 10 taken 267847 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 3583 times.
|
271430 | assert(exprResult.ptr != nullptr || exprResult.refPtr != nullptr); |
| 194 | |||
| 195 | // De-reference if reference type | ||
| 196 |
4/4✓ Branch 10 → 11 taken 256942 times.
✓ Branch 10 → 13 taken 14488 times.
✓ Branch 11 → 12 taken 22 times.
✓ Branch 11 → 13 taken 256920 times.
|
271430 | const bool isVolatile = exprResult.entry && exprResult.entry->isVolatile; |
| 197 |
4/4✓ Branch 14 → 15 taken 3603 times.
✓ Branch 14 → 24 taken 267827 times.
✓ Branch 15 → 16 taken 3583 times.
✓ Branch 15 → 24 taken 20 times.
|
271430 | if (exprResult.refPtr != nullptr && exprResult.ptr == nullptr) |
| 198 |
2/4✓ Branch 19 → 20 taken 3583 times.
✗ Branch 19 → 34 not taken.
✓ Branch 20 → 21 taken 3583 times.
✗ Branch 20 → 34 not taken.
|
3583 | exprResult.ptr = insertLoad(builder.getPtrTy(), exprResult.refPtr, isVolatile); |
| 199 | |||
| 200 | // Load the value from the pointer | ||
| 201 |
1/2✓ Branch 24 → 25 taken 271430 times.
✗ Branch 24 → 46 not taken.
|
271430 | const QualType referencedType = qualType.removeReferenceWrapper(); |
| 202 |
1/2✓ Branch 28 → 29 taken 271430 times.
✗ Branch 28 → 40 not taken.
|
271430 | exprResult.value = insertLoad(referencedType, exprResult.ptr, isVolatile); |
| 203 | |||
| 204 | 271430 | return exprResult.value; | |
| 205 | } | ||
| 206 | |||
| 207 | 22971 | llvm::Value *IRGenerator::resolveAddress(const ASTNode *node) { | |
| 208 | // Visit the given AST node | ||
| 209 |
2/4✓ Branch 2 → 3 taken 22971 times.
✗ Branch 2 → 11 not taken.
✓ Branch 3 → 4 taken 22971 times.
✗ Branch 3 → 9 not taken.
|
22971 | auto exprResult = any_cast<LLVMExprResult>(visit(node)); |
| 210 |
1/2✓ Branch 5 → 6 taken 22971 times.
✗ Branch 5 → 12 not taken.
|
45942 | return resolveAddress(exprResult); |
| 211 | } | ||
| 212 | |||
| 213 | 189577 | llvm::Value *IRGenerator::resolveAddress(LLVMExprResult &exprResult) { | |
| 214 | // Check if an address is already present | ||
| 215 |
2/2✓ Branch 2 → 3 taken 156719 times.
✓ Branch 2 → 4 taken 32858 times.
|
189577 | if (exprResult.ptr != nullptr) |
| 216 | 156719 | return exprResult.ptr; | |
| 217 | |||
| 218 | // Check if the reference address is already present | ||
| 219 |
3/4✓ Branch 4 → 5 taken 25968 times.
✓ Branch 4 → 7 taken 6890 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 25968 times.
|
32858 | const bool isVolatile = exprResult.entry && exprResult.entry->isVolatile; |
| 220 |
3/4✓ Branch 8 → 9 taken 27016 times.
✓ Branch 8 → 18 taken 5842 times.
✓ Branch 9 → 10 taken 27016 times.
✗ Branch 9 → 18 not taken.
|
32858 | if (exprResult.refPtr != nullptr && exprResult.ptr == nullptr) { |
| 221 |
2/4✓ Branch 13 → 14 taken 27016 times.
✗ Branch 13 → 35 not taken.
✓ Branch 14 → 15 taken 27016 times.
✗ Branch 14 → 35 not taken.
|
27016 | exprResult.ptr = insertLoad(builder.getPtrTy(), exprResult.refPtr, isVolatile); |
| 222 | 27016 | return exprResult.ptr; | |
| 223 | } | ||
| 224 | |||
| 225 | // If not, store the value or constant | ||
| 226 | 5842 | materializeConstant(exprResult); | |
| 227 |
1/2✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 5842 times.
|
5842 | assert(exprResult.value != nullptr); |
| 228 |
7/12✓ Branch 21 → 22 taken 8 times.
✓ Branch 21 → 23 taken 5834 times.
✓ Branch 22 → 26 taken 8 times.
✗ Branch 22 → 43 not taken.
✓ Branch 25 → 26 taken 5834 times.
✗ Branch 25 → 43 not taken.
✓ Branch 27 → 28 taken 5842 times.
✗ Branch 27 → 41 not taken.
✓ Branch 29 → 30 taken 5834 times.
✓ Branch 29 → 32 taken 8 times.
✗ Branch 43 → 44 not taken.
✗ Branch 43 → 46 not taken.
|
11676 | exprResult.ptr = insertAlloca(exprResult.value->getType(), exprResult.entry ? exprResult.entry->name : ""); |
| 229 | 5842 | insertStore(exprResult.value, exprResult.ptr, isVolatile); | |
| 230 | |||
| 231 | 5842 | return exprResult.ptr; | |
| 232 | } | ||
| 233 | |||
| 234 | /** | ||
| 235 | * Pack a scalar compile-time constant (integer, float, or a null pointer/aggregate) into a raw byte-array constant of | ||
| 236 | * the given type, using the target's endianness. This is used to embed a union's default field value into its | ||
| 237 | * byte-buffer payload without needing a bitcast between unrelated LLVM types (which is not something opaque-pointer- | ||
| 238 | * era LLVM supports between arbitrary aggregate/scalar types). | ||
| 239 | * | ||
| 240 | * Non-null pointer-shaped constants (e.g. a string literal's global address) cannot be represented this way, since | ||
| 241 | * their real value is only known at link/load time (a relocation) and a plain byte array cannot carry one. | ||
| 242 | * | ||
| 243 | * @param value Compile-time constant to pack | ||
| 244 | * @param byteArrayType Target byte-array type (element type i8) | ||
| 245 | * @return Packed byte-array constant | ||
| 246 | */ | ||
| 247 | 4 | llvm::Constant *IRGenerator::packConstantAsByteArray(llvm::Constant *value, llvm::ArrayType *byteArrayType) const { | |
| 248 |
2/4✓ Branch 3 → 4 taken 4 times.
✗ Branch 3 → 77 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 4 times.
|
4 | assert(byteArrayType->getElementType()->isIntegerTy(8)); |
| 249 | 4 | const uint64_t numBytes = byteArrayType->getNumElements(); | |
| 250 | |||
| 251 | // A null/zero constant (e.g. a null pointer, or a zero-initialized aggregate) packs trivially as all-zero bytes. | ||
| 252 |
1/2✗ Branch 8 → 9 not taken.
✓ Branch 8 → 11 taken 4 times.
|
4 | if (value->isNullValue()) |
| 253 | ✗ | return llvm::Constant::getNullValue(byteArrayType); | |
| 254 | |||
| 255 | 4 | llvm::APInt bits; | |
| 256 |
2/4✓ Branch 12 → 13 taken 4 times.
✗ Branch 12 → 75 not taken.
✓ Branch 13 → 14 taken 4 times.
✗ Branch 13 → 16 not taken.
|
4 | if (const auto *constantInt = llvm::dyn_cast<llvm::ConstantInt>(value)) { |
| 257 |
1/2✓ Branch 15 → 31 taken 4 times.
✗ Branch 15 → 75 not taken.
|
4 | bits = constantInt->getValue(); |
| 258 | ✗ | } else if (const auto *constantFP = llvm::dyn_cast<llvm::ConstantFP>(value)) { | |
| 259 | ✗ | bits = constantFP->getValueAPF().bitcastToAPInt(); | |
| 260 | } else { | ||
| 261 | // See the function comment: this is a rare case in practice (most union field defaults are numeric), so it is | ||
| 262 | // deliberately unsupported for now rather than silently producing a wrong default value. | ||
| 263 | − | throw CompilerError(INTERNAL_ERROR, "Unsupported default value for a union field of this type"); // GCOV_EXCL_LINE | |
| 264 | } | ||
| 265 |
1/2✓ Branch 31 → 32 taken 4 times.
✗ Branch 31 → 66 not taken.
|
4 | bits = bits.zext(numBytes * 8); |
| 266 | |||
| 267 | 4 | const bool isLittleEndian = module->getDataLayout().isLittleEndian(); | |
| 268 |
1/2✓ Branch 38 → 39 taken 4 times.
✗ Branch 38 → 67 not taken.
|
8 | std::vector<uint8_t> bytes(numBytes); |
| 269 |
2/2✓ Branch 49 → 41 taken 32 times.
✓ Branch 49 → 50 taken 4 times.
|
36 | for (uint64_t i = 0; i < numBytes; i++) { |
| 270 |
1/2✓ Branch 41 → 42 taken 32 times.
✗ Branch 41 → 43 not taken.
|
32 | const uint64_t byteIndex = isLittleEndian ? i : numBytes - 1 - i; |
| 271 |
2/4✓ Branch 44 → 45 taken 32 times.
✗ Branch 44 → 72 not taken.
✓ Branch 45 → 46 taken 32 times.
✗ Branch 45 → 70 not taken.
|
32 | bytes[byteIndex] = static_cast<uint8_t>(bits.extractBits(8, i * 8).getZExtValue()); |
| 272 | } | ||
| 273 | |||
| 274 |
1/2✓ Branch 50 → 51 taken 4 times.
✗ Branch 50 → 73 not taken.
|
4 | return llvm::ConstantDataArray::get(context, bytes); |
| 275 | 4 | } | |
| 276 | |||
| 277 | 10360 | llvm::Constant *IRGenerator::getDefaultValueForSymbolType(const QualType &symbolType) { // NOLINT(misc-no-recursion) | |
| 278 | // Double | ||
| 279 |
2/2✓ Branch 3 → 4 taken 96 times.
✓ Branch 3 → 9 taken 10264 times.
|
10360 | if (symbolType.is(TY_DOUBLE)) |
| 280 |
2/4✓ Branch 4 → 5 taken 96 times.
✗ Branch 4 → 174 not taken.
✓ Branch 5 → 6 taken 96 times.
✗ Branch 5 → 172 not taken.
|
96 | return llvm::ConstantFP::get(context, llvm::APFloat(0.0)); |
| 281 | |||
| 282 | // Int | ||
| 283 |
2/2✓ Branch 10 → 11 taken 2062 times.
✓ Branch 10 → 13 taken 8202 times.
|
10264 | if (symbolType.is(TY_INT)) |
| 284 | 2062 | return builder.getInt32(0); | |
| 285 | |||
| 286 | // Short | ||
| 287 |
2/2✓ Branch 14 → 15 taken 32 times.
✓ Branch 14 → 17 taken 8170 times.
|
8202 | if (symbolType.is(TY_SHORT)) |
| 288 | 32 | return builder.getInt16(0); | |
| 289 | |||
| 290 | // Long | ||
| 291 |
2/2✓ Branch 18 → 19 taken 1673 times.
✓ Branch 18 → 21 taken 6497 times.
|
8170 | if (symbolType.is(TY_LONG)) |
| 292 | 1673 | return builder.getInt64(0); | |
| 293 | |||
| 294 | // Byte or char | ||
| 295 |
3/4✓ Branch 21 → 22 taken 6497 times.
✗ Branch 21 → 175 not taken.
✓ Branch 22 → 23 taken 174 times.
✓ Branch 22 → 25 taken 6323 times.
|
6497 | if (symbolType.isOneOf({TY_BYTE, TY_CHAR})) |
| 296 | 174 | return builder.getInt8(0); | |
| 297 | |||
| 298 | // String | ||
| 299 |
2/2✓ Branch 26 → 27 taken 1606 times.
✓ Branch 26 → 35 taken 4717 times.
|
6323 | if (symbolType.is(TY_STRING)) { |
| 300 |
3/6✓ Branch 27 → 28 taken 1606 times.
✗ Branch 27 → 177 not taken.
✓ Branch 28 → 29 taken 1606 times.
✗ Branch 28 → 176 not taken.
✓ Branch 29 → 30 taken 1606 times.
✗ Branch 29 → 176 not taken.
|
1606 | llvm::GlobalVariable *globalString = builder.CreateGlobalString("", ""); |
| 301 |
1/2✓ Branch 30 → 31 taken 1606 times.
✗ Branch 30 → 34 not taken.
|
1606 | if (cliOptions.comparableOutput) |
| 302 |
2/4✓ Branch 31 → 32 taken 1606 times.
✗ Branch 31 → 178 not taken.
✓ Branch 32 → 33 taken 1606 times.
✗ Branch 32 → 178 not taken.
|
1606 | globalString->setAlignment(llvm::Align(4)); |
| 303 | 1606 | return globalString; | |
| 304 | } | ||
| 305 | |||
| 306 | // Bool | ||
| 307 |
2/2✓ Branch 36 → 37 taken 332 times.
✓ Branch 36 → 39 taken 4385 times.
|
4717 | if (symbolType.is(TY_BOOL)) |
| 308 | 332 | return builder.getFalse(); | |
| 309 | |||
| 310 | // Pointer or reference | ||
| 311 |
3/4✓ Branch 39 → 40 taken 4385 times.
✗ Branch 39 → 179 not taken.
✓ Branch 40 → 41 taken 3109 times.
✓ Branch 40 → 44 taken 1276 times.
|
4385 | if (symbolType.isOneOf({TY_PTR, TY_REF})) |
| 312 | 3109 | return llvm::Constant::getNullValue(builder.getPtrTy()); | |
| 313 | |||
| 314 | // Array | ||
| 315 |
2/2✓ Branch 45 → 46 taken 192 times.
✓ Branch 45 → 61 taken 1084 times.
|
1276 | if (symbolType.isArray()) { |
| 316 | // Get array size | ||
| 317 |
1/2✓ Branch 46 → 47 taken 192 times.
✗ Branch 46 → 188 not taken.
|
192 | const size_t arraySize = symbolType.getArraySize(); |
| 318 | |||
| 319 | // Get default value for item | ||
| 320 |
2/4✓ Branch 47 → 48 taken 192 times.
✗ Branch 47 → 180 not taken.
✓ Branch 48 → 49 taken 192 times.
✗ Branch 48 → 180 not taken.
|
192 | llvm::Constant *defaultItemValue = getDefaultValueForSymbolType(symbolType.getContained()); |
| 321 | |||
| 322 | // Retrieve array and item type | ||
| 323 |
2/4✓ Branch 49 → 50 taken 192 times.
✗ Branch 49 → 181 not taken.
✓ Branch 50 → 51 taken 192 times.
✗ Branch 50 → 181 not taken.
|
192 | llvm::Type *itemType = symbolType.getContained().toLLVMType(sourceFile); |
| 324 |
1/2✓ Branch 51 → 52 taken 192 times.
✗ Branch 51 → 188 not taken.
|
192 | llvm::ArrayType *arrayType = llvm::ArrayType::get(itemType, arraySize); |
| 325 | |||
| 326 | // Create a constant array with n times the default value | ||
| 327 |
1/2✓ Branch 54 → 55 taken 192 times.
✗ Branch 54 → 182 not taken.
|
384 | const std::vector itemConstants(arraySize, defaultItemValue); |
| 328 |
1/2✓ Branch 57 → 58 taken 192 times.
✗ Branch 57 → 185 not taken.
|
192 | return llvm::ConstantArray::get(arrayType, itemConstants); |
| 329 | 192 | } | |
| 330 | |||
| 331 | // Function or procedure | ||
| 332 |
3/4✓ Branch 61 → 62 taken 1084 times.
✗ Branch 61 → 189 not taken.
✓ Branch 62 → 63 taken 196 times.
✓ Branch 62 → 70 taken 888 times.
|
1084 | if (symbolType.isOneOf({TY_FUNCTION, TY_PROCEDURE})) { |
| 333 |
2/4✓ Branch 63 → 64 taken 196 times.
✗ Branch 63 → 190 not taken.
✓ Branch 64 → 65 taken 196 times.
✗ Branch 64 → 190 not taken.
|
196 | llvm::Constant *ptrDefaultValue = getDefaultValueForSymbolType(QualType(TY_PTR)); |
| 334 | 196 | llvm::Constant *sizeDefaultValue = builder.getInt64(0); | |
| 335 |
1/2✓ Branch 67 → 68 taken 196 times.
✗ Branch 67 → 191 not taken.
|
196 | return llvm::ConstantStruct::get(llvmTypes.lambdaFatPtrType, {ptrDefaultValue, ptrDefaultValue, sizeDefaultValue}); |
| 336 | } | ||
| 337 | |||
| 338 | // Struct | ||
| 339 |
2/2✓ Branch 71 → 72 taken 830 times.
✓ Branch 71 → 111 taken 58 times.
|
888 | if (symbolType.is(TY_STRUCT)) { |
| 340 | // Retrieve field count | ||
| 341 |
1/2✓ Branch 72 → 73 taken 830 times.
✗ Branch 72 → 198 not taken.
|
830 | Scope *structScope = symbolType.getBodyScope(); |
| 342 |
1/2✗ Branch 73 → 74 not taken.
✓ Branch 73 → 75 taken 830 times.
|
830 | assert(structScope != nullptr); |
| 343 |
1/2✓ Branch 75 → 76 taken 830 times.
✗ Branch 75 → 198 not taken.
|
830 | const size_t fieldCount = structScope->getFieldCount(); |
| 344 | |||
| 345 | // Get default values for all fields of the struct | ||
| 346 | 830 | std::vector<llvm::Constant *> fieldConstants; | |
| 347 |
1/2✓ Branch 76 → 77 taken 830 times.
✗ Branch 76 → 196 not taken.
|
830 | fieldConstants.reserve(fieldCount); |
| 348 | |||
| 349 | // Add default value for each struct field | ||
| 350 |
2/2✓ Branch 103 → 78 taken 2364 times.
✓ Branch 103 → 104 taken 830 times.
|
3194 | for (size_t i = 0; i < fieldCount; i++) { |
| 351 | // Get entry of the field | ||
| 352 |
1/2✗ Branch 78 → 79 not taken.
✓ Branch 78 → 81 taken 2364 times.
|
2364 | const SymbolTableEntry *fieldEntry = structScope->lookupField(i); |
| 353 |
3/6✓ Branch 84 → 85 taken 2364 times.
✗ Branch 84 → 88 not taken.
✓ Branch 85 → 86 taken 2364 times.
✗ Branch 85 → 194 not taken.
✓ Branch 86 → 87 taken 2364 times.
✗ Branch 86 → 88 not taken.
|
2364 | assert(fieldEntry != nullptr && fieldEntry->isField()); |
| 354 | |||
| 355 | // Retrieve default field value | ||
| 356 | llvm::Constant *defaultFieldValue; | ||
| 357 |
5/6✓ Branch 89 → 90 taken 2364 times.
✗ Branch 89 → 91 not taken.
✓ Branch 92 → 93 taken 2330 times.
✓ Branch 92 → 98 taken 34 times.
✓ Branch 93 → 94 taken 1828 times.
✓ Branch 93 → 98 taken 502 times.
|
2364 | if (const auto fieldNode = dynamic_cast<FieldNode *>(fieldEntry->declNode); fieldNode && fieldNode->defaultValue) |
| 358 |
3/6✓ Branch 94 → 95 taken 1828 times.
✗ Branch 94 → 194 not taken.
✓ Branch 95 → 96 taken 1828 times.
✗ Branch 95 → 193 not taken.
✓ Branch 96 → 97 taken 1828 times.
✗ Branch 96 → 193 not taken.
|
1828 | defaultFieldValue = getConst(fieldNode->defaultValue->getCompileTimeValue(manIdx), fieldEntry->getQualType(), fieldNode); |
| 359 | else | ||
| 360 |
2/4✓ Branch 98 → 99 taken 536 times.
✗ Branch 98 → 194 not taken.
✓ Branch 99 → 100 taken 536 times.
✗ Branch 99 → 194 not taken.
|
536 | defaultFieldValue = getDefaultValueForSymbolType(fieldEntry->getQualType()); |
| 361 | |||
| 362 |
1/2✓ Branch 101 → 102 taken 2364 times.
✗ Branch 101 → 194 not taken.
|
2364 | fieldConstants.push_back(defaultFieldValue); |
| 363 | } | ||
| 364 | |||
| 365 |
2/4✓ Branch 104 → 105 taken 830 times.
✗ Branch 104 → 196 not taken.
✓ Branch 105 → 106 taken 830 times.
✗ Branch 105 → 196 not taken.
|
830 | const auto structType = llvm::cast<llvm::StructType>(symbolType.toLLVMType(sourceFile)); |
| 366 |
1/2✓ Branch 107 → 108 taken 830 times.
✗ Branch 107 → 195 not taken.
|
830 | return llvm::ConstantStruct::get(structType, fieldConstants); |
| 367 | 830 | } | |
| 368 | |||
| 369 | // Union | ||
| 370 |
2/2✓ Branch 112 → 113 taken 22 times.
✓ Branch 112 → 155 taken 36 times.
|
58 | if (symbolType.is(TY_UNION)) { |
| 371 | 22 | Scope *unionScope = symbolType.getBodyScope(); | |
| 372 |
1/2✗ Branch 114 → 115 not taken.
✓ Branch 114 → 116 taken 22 times.
|
22 | assert(unionScope != nullptr); |
| 373 | 22 | const size_t fieldCount = unionScope->getFieldCount(); | |
| 374 | |||
| 375 | // Find the (at most one) field with a default value. Tag 0 is reserved for the "unset" state (see the matching | ||
| 376 | // comment in IRGenerator::visitPostfixUnaryExpr's TY_UNION branch), so a field's tag is its index shifted by one. | ||
| 377 | 22 | uint32_t defaultFieldTag = 0; // Sentinel: no field is active ("unset" state) | |
| 378 | 22 | llvm::Constant *defaultFieldValue = nullptr; | |
| 379 |
2/2✓ Branch 139 → 118 taken 40 times.
✓ Branch 139 → 140 taken 18 times.
|
58 | for (size_t i = 0; i < fieldCount; i++) { |
| 380 |
1/2✓ Branch 118 → 119 taken 40 times.
✗ Branch 118 → 121 not taken.
|
40 | const SymbolTableEntry *fieldEntry = unionScope->lookupField(i); |
| 381 |
2/4✓ Branch 124 → 125 taken 40 times.
✗ Branch 124 → 128 not taken.
✓ Branch 126 → 127 taken 40 times.
✗ Branch 126 → 128 not taken.
|
40 | assert(fieldEntry != nullptr && fieldEntry->isField()); |
| 382 |
1/2✓ Branch 129 → 130 taken 40 times.
✗ Branch 129 → 131 not taken.
|
40 | const auto fieldNode = dynamic_cast<FieldNode *>(fieldEntry->declNode); |
| 383 |
3/4✓ Branch 132 → 133 taken 40 times.
✗ Branch 132 → 138 not taken.
✓ Branch 133 → 134 taken 4 times.
✓ Branch 133 → 138 taken 36 times.
|
40 | if (fieldNode && fieldNode->defaultValue) { |
| 384 | 4 | defaultFieldTag = static_cast<uint32_t>(i) + 1; | |
| 385 |
2/4✓ Branch 135 → 136 taken 4 times.
✗ Branch 135 → 199 not taken.
✓ Branch 136 → 137 taken 4 times.
✗ Branch 136 → 199 not taken.
|
4 | defaultFieldValue = getConst(fieldNode->defaultValue->getCompileTimeValue(manIdx), fieldEntry->getQualType(), fieldNode); |
| 386 | 4 | break; | |
| 387 | } | ||
| 388 | } | ||
| 389 | |||
| 390 | 22 | const auto unionType = llvm::cast<llvm::StructType>(symbolType.toLLVMType(sourceFile)); | |
| 391 | |||
| 392 | // With no default field, tag 0 and an all-zero payload is exactly the "unset" state, so the whole constant | ||
| 393 | // collapses to a plain zero value. This also makes such a union eligible for zero-initialized (.bss) storage | ||
| 394 | // when used as a global, rather than requiring an explicit non-zero initializer. | ||
| 395 |
2/2✓ Branch 142 → 143 taken 18 times.
✓ Branch 142 → 145 taken 4 times.
|
22 | if (!defaultFieldValue) |
| 396 | 18 | return llvm::Constant::getNullValue(unionType); | |
| 397 | |||
| 398 | 4 | llvm::Constant *tagConstant = builder.getInt32(defaultFieldTag); | |
| 399 | 4 | llvm::Constant *alignPadConstant = llvm::Constant::getNullValue(unionType->getElementType(1)); | |
| 400 | 4 | auto *payloadType = llvm::cast<llvm::ArrayType>(unionType->getElementType(2)); | |
| 401 | 4 | llvm::Constant *payloadConstant = packConstantAsByteArray(defaultFieldValue, payloadType); | |
| 402 | |||
| 403 |
1/2✓ Branch 152 → 153 taken 4 times.
✗ Branch 152 → 200 not taken.
|
4 | return llvm::ConstantStruct::get(unionType, {tagConstant, alignPadConstant, payloadConstant}); |
| 404 | } | ||
| 405 | |||
| 406 | // Interface | ||
| 407 |
1/2✓ Branch 156 → 157 taken 36 times.
✗ Branch 156 → 163 not taken.
|
36 | if (symbolType.is(TY_INTERFACE)) { |
| 408 | 36 | const auto structType = llvm::cast<llvm::StructType>(symbolType.toLLVMType(sourceFile)); | |
| 409 | 36 | return llvm::ConstantStruct::get(structType, llvm::Constant::getNullValue(builder.getPtrTy())); | |
| 410 | } | ||
| 411 | |||
| 412 | − | throw CompilerError(INTERNAL_ERROR, "Cannot determine default value for symbol type"); // GCOV_EXCL_LINE | |
| 413 | } | ||
| 414 | |||
| 415 | 111320 | llvm::Constant *IRGenerator::getConst(const CompileTimeValue &compileTimeValue, const QualType &type, const ASTNode *node) const { | |
| 416 |
2/2✓ Branch 3 → 4 taken 2752 times.
✓ Branch 3 → 9 taken 108568 times.
|
111320 | if (type.is(TY_DOUBLE)) |
| 417 |
2/4✓ Branch 4 → 5 taken 2752 times.
✗ Branch 4 → 60 not taken.
✓ Branch 5 → 6 taken 2752 times.
✗ Branch 5 → 58 not taken.
|
2752 | return llvm::ConstantFP::get(context, llvm::APFloat(compileTimeValue.doubleValue)); |
| 418 | |||
| 419 |
2/2✓ Branch 10 → 11 taken 19953 times.
✓ Branch 10 → 13 taken 88615 times.
|
108568 | if (type.is(TY_INT)) |
| 420 | 19953 | return builder.getInt32(compileTimeValue.intValue); | |
| 421 | |||
| 422 |
2/2✓ Branch 14 → 15 taken 2766 times.
✓ Branch 14 → 17 taken 85849 times.
|
88615 | if (type.is(TY_SHORT)) |
| 423 | 2766 | return builder.getInt16(compileTimeValue.shortValue); | |
| 424 | |||
| 425 |
2/2✓ Branch 18 → 19 taken 50194 times.
✓ Branch 18 → 21 taken 35655 times.
|
85849 | if (type.is(TY_LONG)) |
| 426 | 50194 | return builder.getInt64(compileTimeValue.longValue); | |
| 427 | |||
| 428 |
3/4✓ Branch 21 → 22 taken 35655 times.
✗ Branch 21 → 61 not taken.
✓ Branch 22 → 23 taken 12097 times.
✓ Branch 22 → 25 taken 23558 times.
|
35655 | if (type.isOneOf({TY_BYTE, TY_CHAR})) |
| 429 | 12097 | return builder.getInt8(compileTimeValue.charValue); | |
| 430 | |||
| 431 |
2/2✓ Branch 26 → 27 taken 15152 times.
✓ Branch 26 → 36 taken 8406 times.
|
23558 | if (type.is(TY_STRING)) { |
| 432 | 15152 | const std::string &stringValue = resourceManager.compileTimeStringValues.at(compileTimeValue.stringValueOffset); | |
| 433 |
2/4✓ Branch 30 → 31 taken 15152 times.
✗ Branch 30 → 64 not taken.
✓ Branch 31 → 32 taken 15152 times.
✗ Branch 31 → 62 not taken.
|
45456 | return createGlobalStringConst(ANON_GLOBAL_STRING_NAME, stringValue, node->codeLoc); |
| 434 | } | ||
| 435 | |||
| 436 |
2/2✓ Branch 37 → 38 taken 7796 times.
✓ Branch 37 → 40 taken 610 times.
|
8406 | if (type.is(TY_BOOL)) |
| 437 | 7796 | return builder.getInt1(compileTimeValue.boolValue); | |
| 438 | |||
| 439 |
2/2✓ Branch 41 → 42 taken 598 times.
✓ Branch 41 → 45 taken 12 times.
|
610 | if (type.is(TY_PTR)) |
| 440 | 598 | return llvm::Constant::getNullValue(builder.getPtrTy()); | |
| 441 | |||
| 442 | // The only compile-time value of a function or procedure type is nil, which is represented as a null fat pointer | ||
| 443 |
2/4✓ Branch 45 → 46 taken 12 times.
✗ Branch 45 → 68 not taken.
✓ Branch 46 → 47 taken 12 times.
✗ Branch 46 → 49 not taken.
|
12 | if (type.isOneOf({TY_FUNCTION, TY_PROCEDURE})) |
| 444 | 12 | return llvm::Constant::getNullValue(llvmTypes.lambdaFatPtrType); | |
| 445 | |||
| 446 | − | throw CompilerError(UNHANDLED_BRANCH, "Constant fall-through"); // GCOV_EXCL_LINE | |
| 447 | } | ||
| 448 | |||
| 449 | 209111 | llvm::BasicBlock *IRGenerator::createBlock(const std::string &blockName /*=""*/) const { | |
| 450 |
2/4✓ Branch 2 → 3 taken 209111 times.
✗ Branch 2 → 7 not taken.
✓ Branch 3 → 4 taken 209111 times.
✗ Branch 3 → 7 not taken.
|
209111 | return llvm::BasicBlock::Create(context, blockName); |
| 451 | } | ||
| 452 | |||
| 453 | 209111 | void IRGenerator::switchToBlock(llvm::BasicBlock *block, llvm::Function *parentFct /*=nullptr*/) { | |
| 454 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 209111 times.
|
209111 | assert(block->getParent() == nullptr); // Ensure that the block was not added to a function already |
| 455 | // If no parent function were passed, use the current function | ||
| 456 |
2/2✓ Branch 5 → 6 taken 148947 times.
✓ Branch 5 → 8 taken 60164 times.
|
209111 | if (!parentFct) |
| 457 | 148947 | parentFct = builder.GetInsertBlock()->getParent(); | |
| 458 | // Append block to current function | ||
| 459 | 209111 | parentFct->insert(parentFct->end(), block); | |
| 460 | // Set insert point to the block | ||
| 461 | 209111 | builder.SetInsertPoint(block); | |
| 462 | 209111 | blockAlreadyTerminated = false; | |
| 463 | 209111 | } | |
| 464 | |||
| 465 | 3527 | void IRGenerator::terminateBlock(const StmtLstNode *stmtLstNode) { | |
| 466 | 3527 | generateScopeCleanup(stmtLstNode); | |
| 467 | 3527 | blockAlreadyTerminated = true; | |
| 468 | 3527 | } | |
| 469 | |||
| 470 | 79835 | void IRGenerator::insertJump(llvm::BasicBlock *targetBlock) { | |
| 471 |
2/2✓ Branch 2 → 3 taken 26632 times.
✓ Branch 2 → 4 taken 53203 times.
|
79835 | if (blockAlreadyTerminated) |
| 472 | 26632 | return; | |
| 473 | 53203 | builder.CreateBr(targetBlock); | |
| 474 | 53203 | blockAlreadyTerminated = true; | |
| 475 | } | ||
| 476 | |||
| 477 | 64156 | void IRGenerator::insertCondJump(llvm::Value *condition, llvm::BasicBlock *trueBlock, llvm::BasicBlock *falseBlock, | |
| 478 | Likelihood likelihood /*=UNSPECIFIED*/) { | ||
| 479 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 64156 times.
|
64156 | if (blockAlreadyTerminated) |
| 480 | ✗ | return; | |
| 481 | 64156 | llvm::CondBrInst *jumpInst = builder.CreateCondBr(condition, trueBlock, falseBlock); | |
| 482 | 64156 | blockAlreadyTerminated = true; | |
| 483 | |||
| 484 |
2/2✓ Branch 5 → 6 taken 7733 times.
✓ Branch 5 → 7 taken 56423 times.
|
64156 | if (likelihood != Likelihood::UNSPECIFIED) |
| 485 | 7733 | mdGenerator.generateBranchWeightsMetadata(jumpInst, likelihood); | |
| 486 | } | ||
| 487 | |||
| 488 | 60102 | void IRGenerator::verifyFunction(const llvm::Function *fct, const CodeLoc &codeLoc) const { | |
| 489 | // Skip the verifying step if the verifier was disabled manually or debug info is emitted | ||
| 490 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 60102 times.
|
60102 | if (cliOptions.disableVerifier) |
| 491 | ✗ | return; | |
| 492 | |||
| 493 | // Verify function | ||
| 494 | 60102 | std::string output; | |
| 495 |
1/2✓ Branch 5 → 6 taken 60102 times.
✗ Branch 5 → 20 not taken.
|
60102 | llvm::raw_string_ostream oss(output); |
| 496 | − | if (llvm::verifyFunction(*fct, &oss)) // LCOV_EXCL_LINE | |
| 497 | − | throw CompilerError(codeLoc, INVALID_FUNCTION, output); // LCOV_EXCL_LINE | |
| 498 | 60102 | } | |
| 499 | |||
| 500 | 4583 | void IRGenerator::verifyModule(const CodeLoc &codeLoc) const { | |
| 501 | // Skip the verifying step if the verifier was disabled manually or debug info is emitted | ||
| 502 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 4583 times.
|
4583 | if (cliOptions.disableVerifier) |
| 503 | ✗ | return; | |
| 504 | |||
| 505 | // Verify module | ||
| 506 | 4583 | std::string output; | |
| 507 |
1/2✓ Branch 5 → 6 taken 4583 times.
✗ Branch 5 → 20 not taken.
|
4583 | llvm::raw_string_ostream oss(output); |
| 508 | − | if (llvm::verifyModule(*module, &oss)) // LCOV_EXCL_LINE | |
| 509 | − | throw CompilerError(codeLoc, INVALID_MODULE, output); // LCOV_EXCL_LINE | |
| 510 | 4583 | } | |
| 511 | |||
| 512 | 35917 | LLVMExprResult IRGenerator::doAssignment(const ASTNode *lhsNode, const ExprNode *rhsNode, const ASTNode *node) { | |
| 513 | // Get entry of left side | ||
| 514 |
2/4✓ Branch 2 → 3 taken 35917 times.
✗ Branch 2 → 18 not taken.
✓ Branch 3 → 4 taken 35917 times.
✗ Branch 3 → 16 not taken.
|
35917 | auto exprResult = std::any_cast<LLVMExprResult>(visit(lhsNode)); |
| 515 | 35917 | const SymbolTableEntry *entry = exprResult.entry; | |
| 516 |
7/10✓ Branch 5 → 6 taken 31166 times.
✓ Branch 5 → 10 taken 4751 times.
✓ Branch 6 → 7 taken 31166 times.
✗ Branch 6 → 19 not taken.
✓ Branch 7 → 8 taken 31166 times.
✗ Branch 7 → 19 not taken.
✓ Branch 8 → 9 taken 1647 times.
✓ Branch 8 → 10 taken 29519 times.
✓ Branch 10 → 11 taken 34270 times.
✗ Branch 10 → 19 not taken.
|
35917 | llvm::Value *lhsAddress = entry != nullptr && entry->getQualType().isRef() ? exprResult.refPtr : resolveAddress(exprResult); |
| 517 |
1/2✓ Branch 12 → 13 taken 35917 times.
✗ Branch 12 → 19 not taken.
|
71834 | return doAssignment(lhsAddress, entry, rhsNode, node); |
| 518 | } | ||
| 519 | |||
| 520 | 81227 | LLVMExprResult IRGenerator::doAssignment(llvm::Value *lhsAddress, const SymbolTableEntry *lhsEntry, const ExprNode *rhsNode, | |
| 521 | const ASTNode *node, bool isDecl) { | ||
| 522 | // Get symbol type of right side | ||
| 523 |
1/2✓ Branch 2 → 3 taken 81227 times.
✗ Branch 2 → 13 not taken.
|
81227 | const QualType &rhsSType = rhsNode->getEvaluatedSymbolType(manIdx); |
| 524 |
2/4✓ Branch 3 → 4 taken 81227 times.
✗ Branch 3 → 12 not taken.
✓ Branch 4 → 5 taken 81227 times.
✗ Branch 4 → 10 not taken.
|
81227 | auto rhs = std::any_cast<LLVMExprResult>(visit(rhsNode)); |
| 525 |
1/2✓ Branch 6 → 7 taken 81227 times.
✗ Branch 6 → 13 not taken.
|
162454 | return doAssignment(lhsAddress, lhsEntry, rhs, rhsSType, node, isDecl); |
| 526 | } | ||
| 527 | |||
| 528 | 81888 | LLVMExprResult IRGenerator::doAssignment(llvm::Value *lhsAddress, const SymbolTableEntry *lhsEntry, LLVMExprResult &rhs, | |
| 529 | const QualType &rhsSType, const ASTNode *node, bool isDecl) { | ||
| 530 | // Deduce some information about the assignment | ||
| 531 |
4/4✓ Branch 2 → 3 taken 77137 times.
✓ Branch 2 → 7 taken 4751 times.
✓ Branch 5 → 6 taken 6033 times.
✓ Branch 5 → 7 taken 71104 times.
|
81888 | const bool isRefAssign = lhsEntry != nullptr && lhsEntry->getQualType().isRef(); |
| 532 | // A non-temporary struct value assigned by value needs a deep copy. This holds whether the destination is a direct | ||
| 533 | // lvalue or the value behind an already-bound reference (assign-through): the binding cases of a reference assignment | ||
| 534 | // (declaration/initial field ref/return value) all return early above before this is consumed, so the remaining | ||
| 535 | // reference assignments are assign-throughs that must copy into the referent instead of shallow-copying (which would | ||
| 536 | // alias the rhs' owned members and double-free). | ||
| 537 | // A reference rhs never denotes a temporary that could be stolen, even without an entry (e.g. a call returning a | ||
| 538 | // reference), because the referenced value is owned elsewhere. | ||
| 539 |
9/12✓ Branch 8 → 9 taken 81888 times.
✗ Branch 8 → 285 not taken.
✓ Branch 9 → 10 taken 81888 times.
✗ Branch 9 → 285 not taken.
✓ Branch 10 → 11 taken 11863 times.
✓ Branch 10 → 16 taken 70025 times.
✓ Branch 11 → 12 taken 11863 times.
✗ Branch 11 → 285 not taken.
✓ Branch 12 → 13 taken 7392 times.
✓ Branch 12 → 15 taken 4471 times.
✓ Branch 14 → 15 taken 133 times.
✓ Branch 14 → 16 taken 7259 times.
|
81888 | const bool needsCopy = rhsSType.removeReferenceWrapper().is(TY_STRUCT) && (rhsSType.isRef() || !rhs.isTemporary()); |
| 540 | |||
| 541 |
2/2✓ Branch 17 → 18 taken 6033 times.
✓ Branch 17 → 59 taken 75855 times.
|
81888 | if (isRefAssign) { |
| 542 |
1/2✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 6033 times.
|
6033 | assert(lhsEntry != nullptr); |
| 543 |
2/2✓ Branch 20 → 21 taken 4386 times.
✓ Branch 20 → 35 taken 1647 times.
|
6033 | if (isDecl) { // Reference gets initially assigned |
| 544 | // Store lhs pointer to rhs | ||
| 545 |
2/4✓ Branch 24 → 25 taken 4386 times.
✗ Branch 24 → 286 not taken.
✓ Branch 25 → 26 taken 4386 times.
✗ Branch 25 → 286 not taken.
|
4386 | llvm::Value *refAddress = insertAlloca(builder.getPtrTy()); |
| 546 | 4386 | updateAddress(lhsEntry, refAddress); | |
| 547 | |||
| 548 | // Generate debug info for variable declaration | ||
| 549 | 4386 | diGenerator.generateLocalVarDebugInfo(lhsEntry->name, refAddress); | |
| 550 | |||
| 551 | // Get address of right side | ||
| 552 | 4386 | llvm::Value *rhsAddress = resolveAddress(rhs); | |
| 553 |
1/2✗ Branch 31 → 32 not taken.
✓ Branch 31 → 33 taken 4386 times.
|
4386 | assert(rhsAddress != nullptr); |
| 554 | 4386 | insertStore(rhsAddress, refAddress); | |
| 555 | |||
| 556 | 4386 | return LLVMExprResult{.value = rhsAddress, .ptr = refAddress, .entry = lhsEntry}; | |
| 557 | } | ||
| 558 | |||
| 559 | // Reference to reference assignment (only for struct fields that are not initialized yet) | ||
| 560 | // These are only allowed inside a ctor body. In other cases, the value of the reference gets assigned, not the ref itself. | ||
| 561 |
6/8✓ Branch 35 → 36 taken 1025 times.
✓ Branch 35 → 42 taken 622 times.
✓ Branch 37 → 38 taken 1023 times.
✓ Branch 37 → 42 taken 2 times.
✓ Branch 38 → 39 taken 1023 times.
✗ Branch 38 → 42 not taken.
✓ Branch 40 → 41 taken 1023 times.
✗ Branch 40 → 42 not taken.
|
1647 | const bool isInitialFieldRefAssign = isInCtorBody && rhsSType.isRef() && rhs.entry && lhsEntry->isField(); |
| 562 | // Assigning the result variable | ||
| 563 | 1647 | const bool isReturnValAssign = lhsEntry->name == RETURN_VARIABLE_NAME; | |
| 564 |
4/4✓ Branch 44 → 45 taken 624 times.
✓ Branch 44 → 46 taken 1023 times.
✓ Branch 45 → 46 taken 24 times.
✓ Branch 45 → 51 taken 600 times.
|
1647 | if (isInitialFieldRefAssign || isReturnValAssign) { |
| 565 | // Get address of right side | ||
| 566 | 1047 | llvm::Value *referencedAddress = resolveAddress(rhs); | |
| 567 |
1/2✗ Branch 47 → 48 not taken.
✓ Branch 47 → 49 taken 1047 times.
|
1047 | assert(referencedAddress != nullptr); |
| 568 | |||
| 569 | // Store the rhs* to the lhs** | ||
| 570 | 1047 | insertStore(referencedAddress, lhsAddress); | |
| 571 | |||
| 572 | 1047 | return LLVMExprResult{.value = referencedAddress, .ptr = lhsAddress, .entry = lhsEntry}; | |
| 573 | } | ||
| 574 | |||
| 575 | // Load referenced address | ||
| 576 |
2/4✓ Branch 54 → 55 taken 600 times.
✗ Branch 54 → 292 not taken.
✓ Branch 55 → 56 taken 600 times.
✗ Branch 55 → 292 not taken.
|
600 | lhsAddress = insertLoad(builder.getPtrTy(), lhsAddress); |
| 577 | } | ||
| 578 | |||
| 579 |
8/8✓ Branch 59 → 60 taken 41585 times.
✓ Branch 59 → 65 taken 34870 times.
✓ Branch 61 → 62 taken 4305 times.
✓ Branch 61 → 65 taken 37280 times.
✓ Branch 63 → 64 taken 4301 times.
✓ Branch 63 → 65 taken 4 times.
✓ Branch 66 → 67 taken 4301 times.
✓ Branch 66 → 88 taken 72154 times.
|
76455 | if (isDecl && rhsSType.is(TY_STRUCT) && rhs.isTemporary()) { |
| 580 |
1/2✗ Branch 67 → 68 not taken.
✓ Branch 67 → 69 taken 4301 times.
|
4301 | assert(lhsEntry != nullptr); |
| 581 |
1/2✓ Branch 69 → 70 taken 4301 times.
✗ Branch 69 → 304 not taken.
|
4301 | llvm::Value *rhsAddress = resolveAddress(rhs); |
| 582 | // Only adopt the temporary's storage directly (temp stealing) unless it is a phi merging two independently | ||
| 583 | // materialized pointers (e.g. a ternary whose branches each own their own storage). Lifetime markers require | ||
| 584 | // their operand to be a real alloca, so stealing such a phi here would emit an invalid llvm.lifetime.end | ||
| 585 | // under --sanitizer=address. Any other pointer (a plain alloca, or one derived from it via GEP/load, as in | ||
| 586 | // the foreach-loop item extraction below) denotes a single, stable piece of storage and is safe to adopt. | ||
| 587 | // The temporary's ownership fully transfers to lhsEntry either way (its underlying storage is never | ||
| 588 | // separately destructed), so falling back to a shallow copy into a dedicated alloca is exactly as correct, | ||
| 589 | // just gives up the pointer-adoption optimization for the phi case. | ||
| 590 |
3/4✓ Branch 70 → 71 taken 4301 times.
✗ Branch 70 → 304 not taken.
✓ Branch 71 → 72 taken 4289 times.
✓ Branch 71 → 75 taken 12 times.
|
4301 | if (!llvm::isa<llvm::PHINode>(rhsAddress)) { |
| 591 | // Directly set the address to the lhs entry (temp stealing) | ||
| 592 |
1/2✓ Branch 72 → 73 taken 4289 times.
✗ Branch 72 → 304 not taken.
|
4289 | updateAddress(lhsEntry, rhsAddress); |
| 593 | 4289 | rhs.entry = lhsEntry; | |
| 594 | // Generate debug info for variable declaration | ||
| 595 |
1/2✓ Branch 73 → 74 taken 4289 times.
✗ Branch 73 → 304 not taken.
|
4289 | diGenerator.generateLocalVarDebugInfo(lhsEntry->name, rhsAddress); |
| 596 | 4289 | return rhs; | |
| 597 | } | ||
| 598 | // Size the copy off lhsEntry's own type, not rhsSType: some callers (e.g. the foreach-loop item | ||
| 599 | // extraction below) pass a struct rhsSType only to steer this branch, while rhs itself resolves (through | ||
| 600 | // refPtr indirection) to a differently-typed value; lhsEntry's type is always the authoritative one here. | ||
| 601 |
1/2✓ Branch 75 → 76 taken 12 times.
✗ Branch 75 → 304 not taken.
|
12 | const QualType &lhsQualType = lhsEntry->getQualType(); |
| 602 |
1/2✓ Branch 79 → 80 taken 12 times.
✗ Branch 79 → 298 not taken.
|
12 | llvm::Value *lhsAddr = insertAlloca(lhsQualType); |
| 603 |
1/2✓ Branch 82 → 83 taken 12 times.
✗ Branch 82 → 304 not taken.
|
12 | updateAddress(lhsEntry, lhsAddr); |
| 604 |
1/2✓ Branch 83 → 84 taken 12 times.
✗ Branch 83 → 304 not taken.
|
12 | diGenerator.generateLocalVarDebugInfo(lhsEntry->name, lhsAddr); |
| 605 |
2/4✓ Branch 84 → 85 taken 12 times.
✗ Branch 84 → 304 not taken.
✓ Branch 85 → 86 taken 12 times.
✗ Branch 85 → 304 not taken.
|
12 | generateShallowCopy(rhsAddress, lhsQualType.toLLVMType(sourceFile), lhsAddr, lhsEntry->isVolatile); |
| 606 | 12 | rhs.ptr = lhsAddr; | |
| 607 | 12 | rhs.entry = lhsEntry; | |
| 608 | 12 | return rhs; | |
| 609 | } | ||
| 610 | |||
| 611 | // Allocate new memory if the lhs address does not exist | ||
| 612 |
2/2✓ Branch 88 → 89 taken 37171 times.
✓ Branch 88 → 100 taken 34983 times.
|
72154 | if (!lhsAddress) { |
| 613 |
1/2✗ Branch 89 → 90 not taken.
✓ Branch 89 → 91 taken 37171 times.
|
37171 | assert(lhsEntry != nullptr); |
| 614 |
2/4✓ Branch 94 → 95 taken 37171 times.
✗ Branch 94 → 305 not taken.
✓ Branch 95 → 96 taken 37171 times.
✗ Branch 95 → 305 not taken.
|
37171 | lhsAddress = insertAlloca(lhsEntry->getQualType()); |
| 615 | 37171 | updateAddress(lhsEntry, lhsAddress); | |
| 616 | // Generate debug info for variable declaration | ||
| 617 | 37171 | diGenerator.generateLocalVarDebugInfo(lhsEntry->name, lhsAddress); | |
| 618 | } | ||
| 619 | |||
| 620 | // Check if we try to assign an array by value to a pointer. Here we have to store the address of the first element to the lhs | ||
| 621 |
10/10✓ Branch 100 → 101 taken 67403 times.
✓ Branch 100 → 109 taken 4751 times.
✓ Branch 103 → 104 taken 13971 times.
✓ Branch 103 → 109 taken 53432 times.
✓ Branch 105 → 106 taken 14 times.
✓ Branch 105 → 109 taken 13957 times.
✓ Branch 107 → 108 taken 6 times.
✓ Branch 107 → 109 taken 8 times.
✓ Branch 110 → 111 taken 6 times.
✓ Branch 110 → 126 taken 72148 times.
|
72154 | if (lhsEntry && lhsEntry->getQualType().isPtr() && rhsSType.isArray() && rhsSType.getArraySize() != ARRAY_SIZE_UNKNOWN) { |
| 622 | // Get address of right side | ||
| 623 |
1/2✓ Branch 111 → 112 taken 6 times.
✗ Branch 111 → 318 not taken.
|
6 | llvm::Value *rhsAddress = resolveAddress(rhs); |
| 624 |
1/2✗ Branch 112 → 113 not taken.
✓ Branch 112 → 114 taken 6 times.
|
6 | assert(rhsAddress != nullptr); |
| 625 |
1/2✓ Branch 114 → 115 taken 6 times.
✗ Branch 114 → 318 not taken.
|
6 | llvm::Type *elementTy = rhsSType.toLLVMType(sourceFile); |
| 626 |
2/4✓ Branch 115 → 116 taken 6 times.
✗ Branch 115 → 318 not taken.
✓ Branch 116 → 117 taken 6 times.
✗ Branch 116 → 318 not taken.
|
6 | llvm::Value *indices[2] = {builder.getInt64(0), builder.getInt32(0)}; |
| 627 |
1/2✓ Branch 121 → 122 taken 6 times.
✗ Branch 121 → 311 not taken.
|
6 | llvm::Value *firstItemAddress = insertInBoundsGEP(elementTy, rhsAddress, indices); |
| 628 |
1/2✓ Branch 124 → 125 taken 6 times.
✗ Branch 124 → 318 not taken.
|
6 | insertStore(firstItemAddress, lhsAddress); |
| 629 | 6 | return LLVMExprResult{.value = rhsAddress, .ptr = lhsAddress, .entry = lhsEntry}; | |
| 630 | } | ||
| 631 | |||
| 632 | // Handle operator overloads | ||
| 633 |
6/6✓ Branch 126 → 127 taken 34870 times.
✓ Branch 126 → 130 taken 37278 times.
✓ Branch 128 → 129 taken 917 times.
✓ Branch 128 → 130 taken 33953 times.
✓ Branch 131 → 132 taken 917 times.
✓ Branch 131 → 148 taken 71231 times.
|
72148 | if (!isDecl && conversionManager.callsOverloadedOpFct(node, DEFAULT_OP_IDX)) { |
| 634 | 917 | ResolverFct lhsV = [&] { return static_cast<llvm::Value *>(nullptr); }; | |
| 635 |
1/2✓ Branch 133 → 134 taken 917 times.
✗ Branch 133 → 319 not taken.
|
917 | ResolverFct rhsV = [&] { return resolveValue(rhsSType, rhs); }; |
| 636 | 1834 | ResolverFct lhsP = [&] { return lhsAddress; }; | |
| 637 | 1834 | ResolverFct rhsP = [&] { return resolveAddress(rhs); }; | |
| 638 | 917 | return conversionManager.callOperatorOverloadFct<2>(node, {lhsV, lhsP, rhsV, rhsP}, DEFAULT_OP_IDX); | |
| 639 | 917 | } | |
| 640 | |||
| 641 | // Check if we need to copy the rhs to the lhs. This happens for structs | ||
| 642 |
2/2✓ Branch 148 → 149 taken 959 times.
✓ Branch 148 → 234 taken 70272 times.
|
71231 | if (needsCopy) { |
| 643 | // Get address of right side | ||
| 644 |
1/2✓ Branch 149 → 150 taken 959 times.
✗ Branch 149 → 395 not taken.
|
959 | llvm::Value *rhsAddress = resolveAddress(rhs); |
| 645 |
1/2✗ Branch 150 → 151 not taken.
✓ Branch 150 → 152 taken 959 times.
|
959 | assert(rhsAddress != nullptr); |
| 646 | |||
| 647 | // If the lhs already holds an initialized, non-trivially-destructible struct, its old value must be | ||
| 648 | // destructed before the copy overwrites it, otherwise its owning members (heap pointers, strings, ...) | ||
| 649 | // would leak. The typechecker only sets a dtor in exactly those cases. To stay correct for a self- | ||
| 650 | // assignment like 'a = a', the destruct + copy are skipped entirely when both sides share the address | ||
| 651 | // (the assignment is a no-op in that case, and destructing first would corrupt the value to copy from). | ||
| 652 |
1/2✓ Branch 152 → 153 taken 959 times.
✗ Branch 152 → 154 not taken.
|
959 | const auto *assignNode = dynamic_cast<const AssignExprNode *>(node); |
| 653 |
3/4✓ Branch 155 → 156 taken 799 times.
✓ Branch 155 → 158 taken 160 times.
✓ Branch 156 → 157 taken 799 times.
✗ Branch 156 → 395 not taken.
|
959 | const Function *lhsDtor = assignNode ? assignNode->lhsDtorFct.at(manIdx) : nullptr; |
| 654 | 959 | llvm::BasicBlock *bCopyEnd = nullptr; | |
| 655 |
2/2✓ Branch 159 → 160 taken 38 times.
✓ Branch 159 → 180 taken 921 times.
|
959 | if (lhsDtor != nullptr) { |
| 656 |
2/4✓ Branch 162 → 163 taken 38 times.
✗ Branch 162 → 339 not taken.
✓ Branch 163 → 164 taken 38 times.
✗ Branch 163 → 337 not taken.
|
76 | llvm::BasicBlock *bCopy = createBlock("assign.copy"); |
| 657 |
2/4✓ Branch 168 → 169 taken 38 times.
✗ Branch 168 → 345 not taken.
✓ Branch 169 → 170 taken 38 times.
✗ Branch 169 → 343 not taken.
|
38 | bCopyEnd = createBlock("assign.copy.end"); |
| 658 |
3/6✓ Branch 172 → 173 taken 38 times.
✗ Branch 172 → 349 not taken.
✓ Branch 173 → 174 taken 38 times.
✗ Branch 173 → 349 not taken.
✓ Branch 174 → 175 taken 38 times.
✗ Branch 174 → 349 not taken.
|
38 | insertCondJump(builder.CreateICmpEQ(lhsAddress, rhsAddress), bCopyEnd, bCopy); |
| 659 |
1/2✓ Branch 175 → 176 taken 38 times.
✗ Branch 175 → 395 not taken.
|
38 | switchToBlock(bCopy); |
| 660 |
1/2✓ Branch 177 → 178 taken 38 times.
✗ Branch 177 → 350 not taken.
|
38 | generateCtorOrDtorCall(lhsAddress, lhsDtor, {}); |
| 661 | } | ||
| 662 | |||
| 663 |
2/4✓ Branch 180 → 181 taken 959 times.
✗ Branch 180 → 353 not taken.
✓ Branch 181 → 182 taken 959 times.
✗ Branch 181 → 353 not taken.
|
959 | const QualType rhsSTypeNonRef = rhsSType.removeReferenceWrapper().toNonConst(); |
| 664 |
3/4✓ Branch 182 → 183 taken 959 times.
✗ Branch 182 → 395 not taken.
✓ Branch 183 → 184 taken 864 times.
✓ Branch 183 → 200 taken 95 times.
|
959 | if (rhsSTypeNonRef.isTriviallyCopyable(node)) { |
| 665 | // Create shallow copy | ||
| 666 |
1/2✓ Branch 184 → 185 taken 864 times.
✗ Branch 184 → 361 not taken.
|
864 | llvm::Type *rhsType = rhsSTypeNonRef.toLLVMType(sourceFile); |
| 667 |
3/10✓ Branch 185 → 186 taken 864 times.
✗ Branch 185 → 187 not taken.
✓ Branch 186 → 190 taken 864 times.
✗ Branch 186 → 354 not taken.
✗ Branch 189 → 190 not taken.
✗ Branch 189 → 354 not taken.
✗ Branch 190 → 191 not taken.
✓ Branch 190 → 193 taken 864 times.
✗ Branch 354 → 355 not taken.
✗ Branch 354 → 357 not taken.
|
864 | const std::string copyName = lhsEntry ? lhsEntry->name : ""; |
| 668 |
3/6✓ Branch 193 → 194 taken 864 times.
✗ Branch 193 → 196 not taken.
✗ Branch 194 → 195 not taken.
✓ Branch 194 → 196 taken 864 times.
✓ Branch 197 → 198 taken 864 times.
✗ Branch 197 → 359 not taken.
|
864 | generateShallowCopy(rhsAddress, rhsType, lhsAddress, lhsEntry && lhsEntry->isVolatile); |
| 669 | 864 | } else { | |
| 670 | // Check if we have a copy ctor | ||
| 671 |
1/2✓ Branch 200 → 201 taken 95 times.
✗ Branch 200 → 394 not taken.
|
95 | Scope *structScope = rhsSTypeNonRef.getBodyScope(); |
| 672 |
2/4✓ Branch 201 → 202 taken 95 times.
✗ Branch 201 → 366 not taken.
✓ Branch 206 → 207 taken 95 times.
✗ Branch 206 → 362 not taken.
|
285 | const ArgList args = {{rhsSTypeNonRef.toConstRef(node), rhs.isTemporary()}}; |
| 673 |
2/4✓ Branch 210 → 211 taken 95 times.
✗ Branch 210 → 370 not taken.
✓ Branch 211 → 212 taken 95 times.
✗ Branch 211 → 368 not taken.
|
95 | const Function *copyCtor = FunctionManager::lookup(structScope, CTOR_FUNCTION_NAME, rhsSTypeNonRef, args, true); |
| 674 |
1/2✓ Branch 214 → 215 taken 95 times.
✗ Branch 214 → 223 not taken.
|
95 | if (copyCtor != nullptr) { |
| 675 | // Call copy ctor | ||
| 676 |
2/4✓ Branch 217 → 218 taken 95 times.
✗ Branch 217 → 376 not taken.
✓ Branch 218 → 219 taken 95 times.
✗ Branch 218 → 374 not taken.
|
190 | generateCtorOrDtorCall(lhsAddress, copyCtor, {rhsAddress}); |
| 677 | } else { | ||
| 678 | ✗ | const std::string structName = rhsSTypeNonRef.getName(); | |
| 679 | ✗ | const std::string msg = "Cannot copy struct '" + structName + "', as it is not trivially copyable and has no copy ctor"; | |
| 680 | ✗ | throw SemanticError(node, COPY_CTOR_REQUIRED, msg); | |
| 681 | ✗ | } | |
| 682 | 95 | } | |
| 683 | |||
| 684 | // Close the self-assignment guard | ||
| 685 |
2/2✓ Branch 230 → 231 taken 38 times.
✓ Branch 230 → 233 taken 921 times.
|
959 | if (bCopyEnd != nullptr) { |
| 686 |
1/2✓ Branch 231 → 232 taken 38 times.
✗ Branch 231 → 395 not taken.
|
38 | insertJump(bCopyEnd); |
| 687 |
1/2✓ Branch 232 → 233 taken 38 times.
✗ Branch 232 → 395 not taken.
|
38 | switchToBlock(bCopyEnd); |
| 688 | } | ||
| 689 | 959 | return LLVMExprResult{.ptr = lhsAddress, .entry = lhsEntry}; | |
| 690 | } | ||
| 691 | |||
| 692 | // If a temporary struct is moved into an already initialized lhs (temp stealing), the old value of the lhs must be | ||
| 693 | // destructed before it gets overwritten, otherwise its owning members would leak. The typechecker only sets a dtor in | ||
| 694 | // exactly those cases. The rhs is already evaluated at this point and, being a temporary, never aliases the lhs. | ||
| 695 |
5/6✓ Branch 235 → 236 taken 1691 times.
✓ Branch 235 → 239 taken 68581 times.
✓ Branch 237 → 238 taken 1691 times.
✗ Branch 237 → 239 not taken.
✓ Branch 240 → 241 taken 1691 times.
✓ Branch 240 → 253 taken 68581 times.
|
70272 | if (rhsSType.is(TY_STRUCT) && rhs.isTemporary()) { |
| 696 |
1/2✓ Branch 241 → 242 taken 1691 times.
✗ Branch 241 → 243 not taken.
|
1691 | const auto *assignNode = dynamic_cast<const AssignExprNode *>(node); |
| 697 |
3/4✓ Branch 244 → 245 taken 1691 times.
✗ Branch 244 → 247 not taken.
✓ Branch 248 → 249 taken 10 times.
✓ Branch 248 → 253 taken 1681 times.
|
1691 | if (const Function *lhsDtor = assignNode ? assignNode->lhsDtorFct.at(manIdx) : nullptr) |
| 698 |
1/2✓ Branch 250 → 251 taken 10 times.
✗ Branch 250 → 396 not taken.
|
10 | generateCtorOrDtorCall(lhsAddress, lhsDtor, {}); |
| 699 | } | ||
| 700 | |||
| 701 | // Optimization: If we have the address of both sides, we can do a memcpy instead of loading and storing the value | ||
| 702 | 70272 | llvm::Value *rhsValue = nullptr; | |
| 703 |
8/8✓ Branch 254 → 255 taken 1691 times.
✓ Branch 254 → 258 taken 68581 times.
✓ Branch 255 → 256 taken 1549 times.
✓ Branch 255 → 258 taken 142 times.
✓ Branch 256 → 257 taken 1539 times.
✓ Branch 256 → 258 taken 10 times.
✓ Branch 259 → 260 taken 1539 times.
✓ Branch 259 → 281 taken 68733 times.
|
70272 | if (rhsSType.is(TY_STRUCT) && rhs.value == nullptr && rhs.constant == nullptr) { |
| 704 | // Create shallow copy | ||
| 705 |
2/4✓ Branch 260 → 261 taken 1539 times.
✗ Branch 260 → 399 not taken.
✓ Branch 261 → 262 taken 1539 times.
✗ Branch 261 → 399 not taken.
|
1539 | const QualType rhsSTypeNonRef = rhsSType.removeReferenceWrapper().toNonConst(); |
| 706 |
1/2✓ Branch 262 → 263 taken 1539 times.
✗ Branch 262 → 407 not taken.
|
1539 | llvm::Type *rhsType = rhsSTypeNonRef.toLLVMType(sourceFile); |
| 707 |
1/2✓ Branch 263 → 264 taken 1539 times.
✗ Branch 263 → 407 not taken.
|
1539 | llvm::Value *rhsAddress = resolveAddress(rhs); |
| 708 |
1/2✗ Branch 264 → 265 not taken.
✓ Branch 264 → 266 taken 1539 times.
|
1539 | assert(rhsAddress != nullptr); |
| 709 |
6/10✓ Branch 266 → 267 taken 705 times.
✓ Branch 266 → 268 taken 834 times.
✓ Branch 267 → 271 taken 705 times.
✗ Branch 267 → 400 not taken.
✓ Branch 270 → 271 taken 834 times.
✗ Branch 270 → 400 not taken.
✓ Branch 271 → 272 taken 834 times.
✓ Branch 271 → 274 taken 705 times.
✗ Branch 400 → 401 not taken.
✗ Branch 400 → 403 not taken.
|
2373 | const std::string copyName = lhsEntry ? lhsEntry->name : ""; |
| 710 |
4/6✓ Branch 274 → 275 taken 705 times.
✓ Branch 274 → 277 taken 834 times.
✗ Branch 275 → 276 not taken.
✓ Branch 275 → 277 taken 705 times.
✓ Branch 278 → 279 taken 1539 times.
✗ Branch 278 → 405 not taken.
|
1539 | generateShallowCopy(rhsAddress, rhsType, lhsAddress, lhsEntry && lhsEntry->isVolatile); |
| 711 | 1539 | } else { | |
| 712 | // We can load the value from the right side and store it to the left side | ||
| 713 | // Retrieve value of the right side | ||
| 714 | 68733 | rhsValue = resolveValue(rhsSType, rhs); | |
| 715 | // Store the value to the address | ||
| 716 | 68733 | insertStore(rhsValue, lhsAddress, rhsSType); | |
| 717 | } | ||
| 718 | |||
| 719 | 70272 | return LLVMExprResult{.value = rhsValue, .ptr = lhsAddress, .entry = lhsEntry}; | |
| 720 |
5/14✓ Branch 136 → 137 taken 917 times.
✗ Branch 136 → 322 not taken.
✓ Branch 137 → 138 taken 917 times.
✗ Branch 137 → 322 not taken.
✓ Branch 138 → 139 taken 917 times.
✗ Branch 138 → 322 not taken.
✓ Branch 139 → 140 taken 917 times.
✗ Branch 139 → 322 not taken.
✓ Branch 140 → 141 taken 917 times.
✗ Branch 140 → 320 not taken.
✗ Branch 322 → 323 not taken.
✗ Branch 322 → 326 not taken.
✗ Branch 324 → 325 not taken.
✗ Branch 324 → 326 not taken.
|
917 | } |
| 721 | |||
| 722 | 2747 | void IRGenerator::generateShallowCopy(llvm::Value *oldAddress, llvm::Type *varType, llvm::Value *targetAddress, | |
| 723 | bool isVolatile) const { | ||
| 724 | // Retrieve size to copy | ||
| 725 |
1/2✓ Branch 3 → 4 taken 2747 times.
✗ Branch 3 → 19 not taken.
|
2747 | const llvm::TypeSize typeSize = module->getDataLayout().getTypeAllocSize(varType); |
| 726 | |||
| 727 | // Create values for memcpy intrinsic | ||
| 728 |
2/4✓ Branch 4 → 5 taken 2747 times.
✗ Branch 4 → 19 not taken.
✓ Branch 5 → 6 taken 2747 times.
✗ Branch 5 → 19 not taken.
|
2747 | llvm::Value *structSize = builder.getInt64(typeSize); |
| 729 |
1/2✓ Branch 6 → 7 taken 2747 times.
✗ Branch 6 → 19 not taken.
|
2747 | llvm::Value *copyVolatile = builder.getInt1(isVolatile); |
| 730 | |||
| 731 | // Call memcpy intrinsic to execute the shallow copy | ||
| 732 |
1/2✓ Branch 7 → 8 taken 2747 times.
✗ Branch 7 → 19 not taken.
|
2747 | llvm::Function *memcpyFct = stdFunctionManager.getMemcpyIntrinsic(); |
| 733 |
1/2✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 2747 times.
|
2747 | assert(targetAddress != nullptr); |
| 734 |
3/6✓ Branch 10 → 11 taken 2747 times.
✗ Branch 10 → 18 not taken.
✓ Branch 12 → 13 taken 2747 times.
✗ Branch 12 → 15 not taken.
✓ Branch 13 → 14 taken 2747 times.
✗ Branch 13 → 15 not taken.
|
2747 | builder.CreateCall(memcpyFct, {targetAddress, oldAddress, structSize, copyVolatile}); |
| 735 | 2747 | } | |
| 736 | |||
| 737 | 152281 | void IRGenerator::autoDeReferencePtr(llvm::Value *&ptr, QualType &symbolType) { | |
| 738 |
6/6✓ Branch 12 → 13 taken 160297 times.
✓ Branch 12 → 15 taken 108970 times.
✓ Branch 14 → 15 taken 8016 times.
✓ Branch 14 → 16 taken 152281 times.
✓ Branch 17 → 3 taken 116986 times.
✓ Branch 17 → 18 taken 152281 times.
|
269267 | while (symbolType.isPtr() || symbolType.isRef()) { |
| 739 |
1/2✓ Branch 6 → 7 taken 116986 times.
✗ Branch 6 → 19 not taken.
|
116986 | ptr = insertLoad(symbolType, ptr); |
| 740 |
1/2✓ Branch 9 → 10 taken 116986 times.
✗ Branch 9 → 25 not taken.
|
116986 | symbolType = symbolType.getContained(); |
| 741 | } | ||
| 742 | 152281 | } | |
| 743 | |||
| 744 | 206 | llvm::GlobalVariable *IRGenerator::createGlobalConst(const std::string &baseName, llvm::Constant *constant) const { | |
| 745 | // Get unused name | ||
| 746 |
1/2✓ Branch 2 → 3 taken 206 times.
✗ Branch 2 → 19 not taken.
|
206 | const std::string globalName = getUnusedGlobalName(baseName); |
| 747 | // Create global | ||
| 748 |
1/2✓ Branch 5 → 6 taken 206 times.
✗ Branch 5 → 15 not taken.
|
206 | module->getOrInsertGlobal(globalName, constant->getType()); |
| 749 |
1/2✓ Branch 7 → 8 taken 206 times.
✗ Branch 7 → 16 not taken.
|
206 | llvm::GlobalVariable *global = module->getNamedGlobal(globalName); |
| 750 | // Set initializer to the given constant | ||
| 751 |
1/2✓ Branch 8 → 9 taken 206 times.
✗ Branch 8 → 17 not taken.
|
206 | global->setInitializer(constant); |
| 752 | 206 | global->setConstant(true); | |
| 753 |
1/2✓ Branch 10 → 11 taken 206 times.
✗ Branch 10 → 17 not taken.
|
206 | global->setLinkage(llvm::GlobalValue::PrivateLinkage); |
| 754 | 206 | global->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); | |
| 755 | 206 | return global; | |
| 756 | 206 | } | |
| 757 | |||
| 758 | 18095 | llvm::GlobalVariable *IRGenerator::createGlobalStringConst(const std::string &baseName, const std::string &value) const { | |
| 759 | // Get unused name | ||
| 760 |
1/2✓ Branch 2 → 3 taken 18095 times.
✗ Branch 2 → 21 not taken.
|
18095 | const std::string globalName = getUnusedGlobalName(baseName); |
| 761 | // Create global | ||
| 762 |
2/4✓ Branch 3 → 4 taken 18095 times.
✗ Branch 3 → 16 not taken.
✓ Branch 5 → 6 taken 18095 times.
✗ Branch 5 → 15 not taken.
|
18095 | builder.CreateGlobalString(value, globalName, 0, module); |
| 763 |
1/2✓ Branch 7 → 8 taken 18095 times.
✗ Branch 7 → 17 not taken.
|
18095 | llvm::GlobalVariable *global = module->getNamedGlobal(globalName); |
| 764 | // If the output should be comparable, fix alignment to 4 bytes | ||
| 765 |
1/2✓ Branch 8 → 9 taken 18095 times.
✗ Branch 8 → 12 not taken.
|
18095 | if (cliOptions.comparableOutput) |
| 766 |
2/4✓ Branch 9 → 10 taken 18095 times.
✗ Branch 9 → 18 not taken.
✓ Branch 10 → 11 taken 18095 times.
✗ Branch 10 → 18 not taken.
|
18095 | global->setAlignment(llvm::Align(4)); |
| 767 | 18095 | return global; | |
| 768 | 18095 | } | |
| 769 | |||
| 770 | 18095 | llvm::GlobalVariable *IRGenerator::createGlobalStringConst(const std::string &baseName, const std::string &value, | |
| 771 | const CodeLoc &codeLoc) const { | ||
| 772 | 18095 | llvm::GlobalVariable *global = createGlobalStringConst(baseName, value); | |
| 773 | // Create debug info | ||
| 774 |
2/2✓ Branch 5 → 6 taken 283 times.
✓ Branch 5 → 12 taken 17812 times.
|
36190 | if (cliOptions.instrumentation.emitsFullDebugInfo()) |
| 775 |
3/6✓ Branch 7 → 8 taken 283 times.
✗ Branch 7 → 16 not taken.
✓ Branch 8 → 9 taken 283 times.
✗ Branch 8 → 16 not taken.
✓ Branch 9 → 10 taken 283 times.
✗ Branch 9 → 14 not taken.
|
283 | diGenerator.generateGlobalStringDebugInfo(global, global->getName().str(), value.length(), codeLoc); |
| 776 | 18095 | return global; | |
| 777 | } | ||
| 778 | |||
| 779 | 29433 | std::string IRGenerator::getUnusedGlobalName(const std::string &baseName) const { | |
| 780 | // Find an unused global name | ||
| 781 | 29433 | std::string globalName; | |
| 782 | 29433 | unsigned int suffixNumber = 0; | |
| 783 | do { | ||
| 784 |
1/2✓ Branch 5 → 6 taken 851744 times.
✗ Branch 5 → 15 not taken.
|
851744 | globalName = baseName + std::to_string(suffixNumber); |
| 785 | 851744 | suffixNumber++; | |
| 786 |
3/4✓ Branch 10 → 11 taken 851744 times.
✗ Branch 10 → 19 not taken.
✓ Branch 11 → 12 taken 822311 times.
✓ Branch 11 → 13 taken 29433 times.
|
851744 | } while (module->getNamedGlobal(globalName) != nullptr); |
| 787 | 29433 | return globalName; | |
| 788 | ✗ | } | |
| 789 | |||
| 790 | 122430 | void IRGenerator::materializeConstant(LLVMExprResult &exprResult) { | |
| 791 | // Skip results, that do not contain a constant or already have a value | ||
| 792 |
3/4✓ Branch 2 → 3 taken 118822 times.
✓ Branch 2 → 4 taken 3608 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 118822 times.
|
122430 | if (exprResult.value != nullptr || exprResult.constant == nullptr) |
| 793 | 3608 | return; | |
| 794 | |||
| 795 | // Default case: the value to the constant | ||
| 796 | 118822 | exprResult.value = exprResult.constant; | |
| 797 | } | ||
| 798 | |||
| 799 | 69831 | bool IRGenerator::isSymbolDSOLocal(bool isPublic) const { | |
| 800 | // If we are compiling a shared library and export the global symbol, we need to drop dso_local | ||
| 801 | // because it may be interposed by other shared objects by the dynamic linker. | ||
| 802 |
3/4✓ Branch 2 → 3 taken 59614 times.
✓ Branch 2 → 4 taken 10217 times.
✓ Branch 3 → 4 taken 59614 times.
✗ Branch 3 → 5 not taken.
|
69831 | return !(isPublic && cliOptions.outputContainer == OutputContainer::SHARED_LIBRARY); |
| 803 | } | ||
| 804 | |||
| 805 | 64221 | llvm::GlobalValue::LinkageTypes IRGenerator::getSymbolLinkageType(bool isPublic) const { | |
| 806 |
2/2✓ Branch 2 → 3 taken 54196 times.
✓ Branch 2 → 4 taken 10025 times.
|
64221 | return isPublic ? llvm::GlobalValue::ExternalLinkage : llvm::GlobalValue::InternalLinkage; |
| 807 | } | ||
| 808 | |||
| 809 | 6633 | llvm::GlobalValue::LinkageTypes IRGenerator::getVTableLinkageType(bool isPublic) const { | |
| 810 | // VTables, type infos and type info names are ODR entities that may legitimately be emitted in more than one | ||
| 811 | // translation unit (e.g. an interface that is defined in one file and used from several importing files). Giving | ||
| 812 | // them weak ODR linkage lets the linker coalesce the duplicates. On ELF this pairs with the comdat group below; on | ||
| 813 | // MachO, which has no comdat support, the weak/coalesced linkage is what prevents a duplicate-symbol error. | ||
| 814 |
2/2✓ Branch 2 → 3 taken 6441 times.
✓ Branch 2 → 4 taken 192 times.
|
6633 | return isPublic ? llvm::GlobalValue::WeakODRLinkage : llvm::GlobalValue::PrivateLinkage; |
| 815 | } | ||
| 816 | |||
| 817 | 6633 | void IRGenerator::attachComdatToSymbol(llvm::GlobalVariable *global, const std::string &comdatName, bool isPublic) const { | |
| 818 | // MachO does not support comdat annotations | ||
| 819 |
6/6✓ Branch 2 → 3 taken 6441 times.
✓ Branch 2 → 6 taken 192 times.
✓ Branch 4 → 5 taken 6411 times.
✓ Branch 4 → 6 taken 30 times.
✓ Branch 7 → 8 taken 6411 times.
✓ Branch 7 → 12 taken 222 times.
|
6633 | if (isPublic && cliOptions.targetTriple.getObjectFormat() != llvm::Triple::MachO) |
| 820 |
2/4✓ Branch 9 → 10 taken 6411 times.
✗ Branch 9 → 13 not taken.
✓ Branch 10 → 11 taken 6411 times.
✗ Branch 10 → 13 not taken.
|
6411 | global->setComdat(module->getOrInsertComdat(comdatName)); |
| 821 | 6633 | } | |
| 822 | |||
| 823 | /** | ||
| 824 | * Attach the function attributes that all functions we emit have in common. | ||
| 825 | * | ||
| 826 | * Spice does not know exceptions and we never emit landing pads, so none of our functions can unwind. We still request | ||
| 827 | * an unwind table, so that debuggers and profilers are able to produce correct stack traces. | ||
| 828 | * | ||
| 829 | * Frame pointers are off by default and are requested per function by '--keep-frame-pointers'. This attribute is the | ||
| 830 | * only thing the backend reads. The 'frame-pointer' module flag that IRGenerator's constructor sets from the same | ||
| 831 | * option covers a different set of functions - Function::createWithDefaultAttr() stamps it onto the functions LLVM | ||
| 832 | * itself synthesizes, such as the sanitizer module ctors - so both have to be set, and the module flag alone changes | ||
| 833 | * nothing about the functions we emit: x86-64 Linux then omits the frame pointer at every optimization level, and | ||
| 834 | * AArch64 spills the frame record but never links the chain up. | ||
| 835 | * | ||
| 836 | * Nothing is emitted in the default case, rather than an explicit "none", which is exactly what LLVM does for its own | ||
| 837 | * synthesized functions. It also leaves the decision to the backend, so targets whose ABI mandates a frame pointer | ||
| 838 | * (e.g. AArch64 on Darwin) keep theirs. | ||
| 839 | * | ||
| 840 | * The size levels are not communicated to LLVM by the pass pipeline alone - since Os and Oz both select the O2 | ||
| 841 | * pipeline, 'optsize' and 'minsize' on the individual function are what actually distinguishes them. Without 'minsize', | ||
| 842 | * Oz is indistinguishable from Os. | ||
| 843 | * | ||
| 844 | * @param fct Function to attach the attributes to | ||
| 845 | * @param isAlwaysInline Whether the function was declared as inline | ||
| 846 | */ | ||
| 847 | 60164 | void IRGenerator::addCommonFctAttrs(llvm::Function *fct, bool isAlwaysInline) const { | |
| 848 | 60164 | fct->addFnAttr(llvm::Attribute::NoUnwind); | |
| 849 | 60164 | fct->addFnAttr(llvm::Attribute::getWithUWTableKind(context, llvm::UWTableKind::Default)); | |
| 850 |
2/2✓ Branch 5 → 6 taken 4 times.
✓ Branch 5 → 10 taken 60160 times.
|
60164 | if (cliOptions.keepFramePointers) |
| 851 |
3/6✓ Branch 6 → 7 taken 4 times.
✗ Branch 6 → 21 not taken.
✓ Branch 7 → 8 taken 4 times.
✗ Branch 7 → 20 not taken.
✓ Branch 8 → 9 taken 4 times.
✗ Branch 8 → 20 not taken.
|
4 | fct->addFnAttr("frame-pointer", "all"); |
| 852 | |||
| 853 | // Explicitly inlined functions must not be marked as 'optnone', because that is incompatible with 'alwaysinline'. | ||
| 854 | // This matches the behavior of other frontends: an inline request is honored, even at O0. | ||
| 855 |
2/2✓ Branch 10 → 11 taken 15690 times.
✓ Branch 10 → 12 taken 44474 times.
|
60164 | if (isAlwaysInline) { |
| 856 | 15690 | fct->addFnAttr(llvm::Attribute::AlwaysInline); | |
| 857 |
2/2✓ Branch 12 → 13 taken 44364 times.
✓ Branch 12 → 15 taken 110 times.
|
44474 | } else if (cliOptions.optLevel == OptLevel::O0) { |
| 858 | 44364 | fct->addFnAttr(llvm::Attribute::OptimizeNone); | |
| 859 | 44364 | fct->addFnAttr(llvm::Attribute::NoInline); // 'optnone' requires 'noinline' | |
| 860 | } | ||
| 861 | |||
| 862 |
2/2✓ Branch 15 → 16 taken 4 times.
✓ Branch 15 → 19 taken 60160 times.
|
60164 | if (cliOptions.optLevel >= OptLevel::Os) { |
| 863 | 4 | fct->addFnAttr(llvm::Attribute::OptimizeForSize); | |
| 864 |
2/2✓ Branch 17 → 18 taken 2 times.
✓ Branch 17 → 19 taken 2 times.
|
4 | if (cliOptions.optLevel == OptLevel::Oz) |
| 865 | 2 | fct->addFnAttr(llvm::Attribute::MinSize); | |
| 866 | } | ||
| 867 | 60164 | } | |
| 868 | |||
| 869 | 422282 | llvm::Value *IRGenerator::getAddress(const SymbolTableEntry *entry) { | |
| 870 |
1/2✓ Branch 2 → 3 taken 422282 times.
✗ Branch 2 → 18 not taken.
|
422282 | const auto it = addressMap.find(entry); |
| 871 |
5/6✓ Branch 5 → 6 taken 421075 times.
✓ Branch 5 → 9 taken 1207 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 421075 times.
✓ Branch 11 → 12 taken 1207 times.
✓ Branch 11 → 13 taken 421075 times.
|
422282 | if (it == addressMap.end() || it->second.empty()) |
| 872 | 1207 | return nullptr; | |
| 873 | 421075 | return it->second.top(); | |
| 874 | } | ||
| 875 | |||
| 876 | 265199 | void IRGenerator::updateAddress(const SymbolTableEntry *entry, llvm::Value *address) { | |
| 877 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 265199 times.
|
265199 | assert(address != nullptr); |
| 878 |
1/2✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 265199 times.
|
265199 | assert(address->getType()->isPointerTy()); |
| 879 | 265199 | auto &stack = addressMap[entry]; | |
| 880 |
2/2✓ Branch 10 → 11 taken 207445 times.
✓ Branch 10 → 12 taken 57754 times.
|
265199 | if (stack.empty()) |
| 881 | 207445 | stack.push(address); | |
| 882 | else | ||
| 883 | 57754 | stack.top() = address; | |
| 884 | 265199 | } | |
| 885 | |||
| 886 | 89 | void IRGenerator::pushAddress(const SymbolTableEntry *entry, llvm::Value *address) { | |
| 887 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 89 times.
|
89 | assert(address != nullptr); |
| 888 | 89 | addressMap[entry].push(address); | |
| 889 | 89 | } | |
| 890 | |||
| 891 | 89 | void IRGenerator::popAddress(const SymbolTableEntry *entry) { | |
| 892 |
1/2✓ Branch 2 → 3 taken 89 times.
✗ Branch 2 → 14 not taken.
|
89 | auto it = addressMap.find(entry); |
| 893 |
2/4✓ Branch 5 → 6 taken 89 times.
✗ Branch 5 → 10 not taken.
✓ Branch 8 → 9 taken 89 times.
✗ Branch 8 → 10 not taken.
|
89 | assert(it != addressMap.end() && !it->second.empty()); |
| 894 | 89 | it->second.pop(); | |
| 895 | 89 | } | |
| 896 | |||
| 897 | 5100 | llvm::Function *IRGenerator::getLLVMFunction(const Function *spiceFunc) { | |
| 898 |
1/2✓ Branch 2 → 3 taken 5100 times.
✗ Branch 2 → 12 not taken.
|
5100 | const auto it = llvmFunctions.find(spiceFunc); |
| 899 |
2/2✓ Branch 5 → 6 taken 2528 times.
✓ Branch 5 → 8 taken 2572 times.
|
10200 | return it != llvmFunctions.end() ? it->second : nullptr; |
| 900 | } | ||
| 901 | |||
| 902 | 57981 | void IRGenerator::setLLVMFunction(const Function *spiceFunc, llvm::Function *llvmFunction) { | |
| 903 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 57981 times.
|
57981 | assert(llvmFunction != nullptr); |
| 904 | 57981 | llvmFunctions[spiceFunc] = llvmFunction; | |
| 905 | 57981 | } | |
| 906 | |||
| 907 | 8878 | std::string IRGenerator::getIRString(llvm::Module *llvmModule, const CliOptions &cliOptions) { | |
| 908 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 8878 times.
|
8878 | assert(llvmModule != nullptr); // Make sure the module hasn't been moved away |
| 909 |
3/4✓ Branch 4 → 5 taken 8878 times.
✗ Branch 4 → 7 not taken.
✓ Branch 5 → 6 taken 8666 times.
✓ Branch 5 → 7 taken 212 times.
|
8878 | const bool eliminateTarget = cliOptions.comparableOutput && cliOptions.isNativeTarget; |
| 910 | |||
| 911 | // Backup target triple and data layout | ||
| 912 |
1/2✓ Branch 9 → 10 taken 8878 times.
✗ Branch 9 → 45 not taken.
|
8878 | const llvm::Triple targetTriple = llvmModule->getTargetTriple(); |
| 913 |
1/2✓ Branch 11 → 12 taken 8878 times.
✗ Branch 11 → 43 not taken.
|
8878 | const std::string targetDataLayout = llvmModule->getDataLayoutStr(); |
| 914 | // Remove target triple and data layout | ||
| 915 |
2/2✓ Branch 12 → 13 taken 8666 times.
✓ Branch 12 → 19 taken 212 times.
|
8878 | if (eliminateTarget) { |
| 916 | 8666 | llvmModule->setTargetTriple(llvm::Triple()); | |
| 917 |
2/4✓ Branch 16 → 17 taken 8666 times.
✗ Branch 16 → 34 not taken.
✓ Branch 17 → 18 taken 8666 times.
✗ Branch 17 → 34 not taken.
|
8666 | llvmModule->setDataLayout(""); |
| 918 | } | ||
| 919 | |||
| 920 | // Get IR string | ||
| 921 | 8878 | std::string output; | |
| 922 |
1/2✓ Branch 20 → 21 taken 8878 times.
✗ Branch 20 → 39 not taken.
|
8878 | llvm::raw_string_ostream oss(output); |
| 923 |
1/2✓ Branch 21 → 22 taken 8878 times.
✗ Branch 21 → 37 not taken.
|
8878 | llvmModule->print(oss, nullptr); |
| 924 | |||
| 925 | // Restore target triple and data layout | ||
| 926 |
2/2✓ Branch 22 → 23 taken 8666 times.
✓ Branch 22 → 29 taken 212 times.
|
8878 | if (eliminateTarget) { |
| 927 |
1/2✓ Branch 23 → 24 taken 8666 times.
✗ Branch 23 → 35 not taken.
|
8666 | llvmModule->setTargetTriple(targetTriple); |
| 928 |
1/2✓ Branch 27 → 28 taken 8666 times.
✗ Branch 27 → 36 not taken.
|
8666 | llvmModule->setDataLayout(targetDataLayout); |
| 929 | } | ||
| 930 | |||
| 931 | 8878 | return output; | |
| 932 | 8878 | } | |
| 933 | |||
| 934 | /** | ||
| 935 | * Returns the operator function list for the current manifestation and the given node | ||
| 936 | * | ||
| 937 | * @param node Node to retrieve the op fct pointer list from | ||
| 938 | * @return Op fct pointer list | ||
| 939 | */ | ||
| 940 | 146948 | const std::vector<const Function *> &IRGenerator::getOpFctPointers(const ASTNode *node) const { | |
| 941 |
1/2✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 146948 times.
|
146948 | assert(node->getOpFctPointers()->size() > manIdx); |
| 942 | 146948 | return node->getOpFctPointers()->at(manIdx); | |
| 943 | } | ||
| 944 | |||
| 945 | } // namespace spice::compiler | ||
| 946 |