GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 85.1% 445 / 3 / 526
Functions: 93.0% 40 / 0 / 43
Branches: 46.3% 462 / 12 / 1010

src/SourceFile.cpp
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 #include "SourceFile.h"
4
5 #include <ast/ASTBuilder.h>
6 #include <driver/Driver.h>
7 #include <exception/AntlrThrowingErrorListener.h>
8 #include <exception/CompilerError.h>
9 #include <global/GlobalResourceManager.h>
10 #include <global/TypeRegistry.h>
11 #include <importcollector/ImportCollector.h>
12 #include <irgenerator/IRGenerator.h>
13 #include <iroptimizer/IROptimizer.h>
14 #include <linker/BitcodeLinker.h>
15 #include <objectemitter/ObjectEmitter.h>
16 #include <symboltablebuilder/SymbolTable.h>
17 #include <symboltablebuilder/SymbolTableBuilder.h>
18 #include <typechecker/FunctionManager.h>
19 #include <typechecker/InterfaceManager.h>
20 #include <typechecker/MacroDefs.h>
21 #include <typechecker/StructManager.h>
22 #include <typechecker/TypeChecker.h>
23 #include <util/CompilerWarning.h>
24 #include <util/FileUtil.h>
25 #include <util/SystemUtil.h>
26 #include <util/Timer.h>
27 #include <visualizer/ASTVisualizer.h>
28 #include <visualizer/CSTVisualizer.h>
29 #include <visualizer/DependencyGraphVisualizer.h>
30
31 #include <llvm/IR/Module.h>
32 #include <llvm/MC/TargetRegistry.h>
33
34 namespace spice::compiler {
35
36 1243 SourceFile::SourceFile(GlobalResourceManager &resourceManager, SourceFile *parent, std::string name,
37 1243 const std::filesystem::path &filePath, bool stdFile)
38
1/2
✓ Branch 5 → 6 taken 1243 times.
✗ Branch 5 → 118 not taken.
2486 : name(std::move(name)), filePath(filePath), isStdFile(stdFile), parent(parent),
39
3/4
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 15 taken 1241 times.
✓ Branch 16 → 17 taken 1243 times.
✗ Branch 16 → 58 not taken.
1243 builder(resourceManager.cliOptions.useLTO ? resourceManager.ltoContext : context), resourceManager(resourceManager),
40
1/2
✓ Branch 11 → 12 taken 1243 times.
✗ Branch 11 → 106 not taken.
3729 cliOptions(resourceManager.cliOptions) {
41 // Deduce fileName and fileDir
42
3/6
✓ Branch 24 → 25 taken 1243 times.
✗ Branch 24 → 63 not taken.
✓ Branch 25 → 26 taken 1243 times.
✗ Branch 25 → 61 not taken.
✓ Branch 26 → 27 taken 1243 times.
✗ Branch 26 → 59 not taken.
1243 fileName = std::filesystem::path(filePath).filename().string();
43
3/6
✓ Branch 31 → 32 taken 1243 times.
✗ Branch 31 → 70 not taken.
✓ Branch 32 → 33 taken 1243 times.
✗ Branch 32 → 68 not taken.
✓ Branch 33 → 34 taken 1243 times.
✗ Branch 33 → 66 not taken.
1243 fileDir = std::filesystem::path(filePath).parent_path().string();
44
45 // Search after the selected target
46 1243 std::string error;
47
1/2
✓ Branch 39 → 40 taken 1243 times.
✗ Branch 39 → 85 not taken.
1243 const llvm::Target *target = llvm::TargetRegistry::lookupTarget(cliOptions.targetTriple, error);
48
1/2
✗ Branch 40 → 41 not taken.
✓ Branch 40 → 46 taken 1243 times.
1243 if (!target)
49 throw CompilerError(TARGET_NOT_AVAILABLE, "Selected target was not found: " + error); // GCOV_EXCL_LINE
50
51 // Create the target machine
52
1/2
✓ Branch 46 → 47 taken 1243 times.
✗ Branch 46 → 85 not taken.
1243 llvm::TargetOptions opt;
53 1243 opt.MCOptions.AsmVerbose = true;
54 1243 opt.MCOptions.PreserveAsmComments = true;
55 1243 const std::string &cpuName = resourceManager.cpuName;
56 1243 const std::string &features = resourceManager.cpuFeatures;
57 1243 const llvm::Triple &targetTriple = cliOptions.targetTriple;
58 1243 constexpr llvm::Reloc::Model relocModel = llvm::Reloc::PIC_;
59
1/2
✓ Branch 51 → 52 taken 1243 times.
✗ Branch 51 → 79 not taken.
1243 llvm::TargetMachine *targetMachineRaw = target->createTargetMachine(targetTriple, cpuName, features, opt, relocModel);
60 1243 targetMachine = std::unique_ptr<llvm::TargetMachine>(targetMachineRaw);
61 1243 }
62
63 1454 void SourceFile::runLexer() {
64
2/2
✓ Branch 2 → 3 taken 449 times.
✓ Branch 2 → 4 taken 1005 times.
1454 if (isMainFile)
65
1/2
✓ Branch 3 → 4 taken 449 times.
✗ Branch 3 → 89 not taken.
449 resourceManager.totalTimer.start();
66
67 // Check if this stage has already been done
68
2/2
✓ Branch 4 → 5 taken 213 times.
✓ Branch 4 → 6 taken 1241 times.
1454 if (previousStage >= LEXER)
69 213 return;
70
71
1/2
✓ Branch 6 → 7 taken 1241 times.
✗ Branch 6 → 89 not taken.
1241 Timer timer(&compilerOutput.times.lexer);
72
1/2
✓ Branch 7 → 8 taken 1241 times.
✗ Branch 7 → 89 not taken.
1241 timer.start();
73
74 // Read from the input source file
75
1/2
✓ Branch 8 → 9 taken 1241 times.
✗ Branch 8 → 89 not taken.
1241 std::ifstream fileInputStream(filePath);
76
3/4
✓ Branch 9 → 10 taken 1241 times.
✗ Branch 9 → 87 not taken.
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 20 taken 1240 times.
1241 if (!fileInputStream)
77
4/8
✓ Branch 12 → 13 taken 1 time.
✗ Branch 12 → 65 not taken.
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 63 not taken.
✓ Branch 14 → 15 taken 1 time.
✗ Branch 14 → 61 not taken.
✓ Branch 15 → 16 taken 1 time.
✗ Branch 15 → 58 not taken.
1 throw CompilerError(SOURCE_FILE_NOT_FOUND, "Source file at path '" + filePath.string() + "' does not exist.");
78
79 // Tokenize input
80
1/2
✓ Branch 20 → 21 taken 1240 times.
✗ Branch 20 → 70 not taken.
1240 antlrCtx.inputStream = std::make_unique<antlr4::ANTLRInputStream>(fileInputStream);
81
1/2
✓ Branch 24 → 25 taken 1240 times.
✗ Branch 24 → 71 not taken.
1240 antlrCtx.lexer = std::make_unique<SpiceLexer>(antlrCtx.inputStream.get());
82
1/2
✓ Branch 28 → 29 taken 1240 times.
✗ Branch 28 → 87 not taken.
1240 antlrCtx.lexer->removeErrorListeners();
83
1/2
✓ Branch 29 → 30 taken 1240 times.
✗ Branch 29 → 73 not taken.
1240 antlrCtx.lexerErrorHandler = std::make_unique<AntlrThrowingErrorListener>(ThrowingErrorListenerMode::LEXER, this);
84
1/2
✓ Branch 34 → 35 taken 1240 times.
✗ Branch 34 → 87 not taken.
1240 antlrCtx.lexer->addErrorListener(antlrCtx.lexerErrorHandler.get());
85
1/2
✓ Branch 36 → 37 taken 1240 times.
✗ Branch 36 → 76 not taken.
1240 antlrCtx.tokenStream = std::make_unique<antlr4::CommonTokenStream>(antlrCtx.lexer.get());
86
87 // Calculate cache key
88
1/2
✓ Branch 39 → 40 taken 1240 times.
✗ Branch 39 → 87 not taken.
1240 std::stringstream cacheKeyString;
89
4/6
✓ Branch 40 → 41 taken 1240 times.
✗ Branch 40 → 85 not taken.
✓ Branch 42 → 43 taken 1239 times.
✓ Branch 42 → 80 taken 1 time.
✓ Branch 44 → 45 taken 1239 times.
✗ Branch 44 → 78 not taken.
1240 cacheKeyString << std::hex << std::hash<std::string>{}(antlrCtx.tokenStream->getText());
90
1/2
✓ Branch 46 → 47 taken 1239 times.
✗ Branch 46 → 82 not taken.
1239 cacheKey = cacheKeyString.str();
91
92 // Try to load from cache
93
1/2
✗ Branch 49 → 50 not taken.
✓ Branch 49 → 52 taken 1239 times.
1239 if (!cliOptions.ignoreCache)
94 restoredFromCache = resourceManager.cacheManager.lookupSourceFile(this);
95
96 1239 previousStage = LEXER;
97
1/2
✓ Branch 52 → 53 taken 1239 times.
✗ Branch 52 → 85 not taken.
1239 timer.stop();
98
1/2
✓ Branch 53 → 54 taken 1239 times.
✗ Branch 53 → 83 not taken.
1239 printStatusMessage("Lexer", IO_CODE, IO_TOKENS, compilerOutput.times.lexer);
99 1242 }
100
101 1452 void SourceFile::runParser() {
102 // Skip if restored from the cache or this stage has already been done
103
3/4
✓ Branch 2 → 3 taken 1452 times.
✗ Branch 2 → 4 not taken.
✓ Branch 3 → 4 taken 213 times.
✓ Branch 3 → 5 taken 1239 times.
1452 if (restoredFromCache || previousStage >= PARSER)
104 213 return;
105
106
1/2
✓ Branch 5 → 6 taken 1239 times.
✗ Branch 5 → 32 not taken.
1239 Timer timer(&compilerOutput.times.parser);
107
1/2
✓ Branch 6 → 7 taken 1239 times.
✗ Branch 6 → 32 not taken.
1239 timer.start();
108
109 // Parse input
110
1/2
✓ Branch 8 → 9 taken 1239 times.
✗ Branch 8 → 25 not taken.
1239 antlrCtx.parser = std::make_unique<SpiceParser>(antlrCtx.tokenStream.get()); // Check for syntax errors
111
1/2
✓ Branch 12 → 13 taken 1239 times.
✗ Branch 12 → 32 not taken.
1239 antlrCtx.parser->removeErrorListeners();
112
1/2
✓ Branch 13 → 14 taken 1239 times.
✗ Branch 13 → 27 not taken.
1239 antlrCtx.parserErrorHandler = std::make_unique<AntlrThrowingErrorListener>(ThrowingErrorListenerMode::PARSER, this);
113
1/2
✓ Branch 18 → 19 taken 1239 times.
✗ Branch 18 → 32 not taken.
1239 antlrCtx.parser->addErrorListener(antlrCtx.parserErrorHandler.get());
114
1/2
✓ Branch 20 → 21 taken 1239 times.
✗ Branch 20 → 32 not taken.
1239 antlrCtx.parser->removeParseListeners();
115
116 1239 previousStage = PARSER;
117
1/2
✓ Branch 21 → 22 taken 1239 times.
✗ Branch 21 → 32 not taken.
1239 timer.stop();
118
1/2
✓ Branch 22 → 23 taken 1239 times.
✗ Branch 22 → 30 not taken.
1239 printStatusMessage("Parser", IO_TOKENS, IO_CST, compilerOutput.times.parser);
119 }
120
121 1013 void SourceFile::runCSTVisualizer() {
122 // Only execute if enabled
123
3/6
✓ Branch 2 → 3 taken 1013 times.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 1013 times.
✗ Branch 3 → 6 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1013 times.
1013 if (restoredFromCache || (!cliOptions.dump.dumpCST && !cliOptions.testMode))
124 213 return;
125 // Check if this stage has already been done
126
2/2
✓ Branch 6 → 7 taken 213 times.
✓ Branch 6 → 8 taken 800 times.
1013 if (previousStage >= CST_VISUALIZER)
127 213 return;
128
129
1/2
✓ Branch 8 → 9 taken 800 times.
✗ Branch 8 → 66 not taken.
800 Timer timer(&compilerOutput.times.cstVisualizer);
130
1/2
✓ Branch 9 → 10 taken 800 times.
✗ Branch 9 → 66 not taken.
800 timer.start();
131
132 // Generate dot code for this source file
133
1/2
✓ Branch 10 → 11 taken 800 times.
✗ Branch 10 → 66 not taken.
800 std::stringstream dotCode;
134
1/2
✓ Branch 11 → 12 taken 800 times.
✗ Branch 11 → 64 not taken.
800 visualizerPreamble(dotCode);
135
1/2
✓ Branch 14 → 15 taken 800 times.
✗ Branch 14 → 64 not taken.
800 CSTVisualizer cstVisualizer(resourceManager, this, antlrCtx.lexer.get(), antlrCtx.parser.get());
136
6/12
✓ Branch 15 → 16 taken 800 times.
✗ Branch 15 → 62 not taken.
✓ Branch 17 → 18 taken 800 times.
✗ Branch 17 → 51 not taken.
✓ Branch 18 → 19 taken 800 times.
✗ Branch 18 → 51 not taken.
✓ Branch 19 → 20 taken 800 times.
✗ Branch 19 → 49 not taken.
✓ Branch 20 → 21 taken 800 times.
✗ Branch 20 → 47 not taken.
✓ Branch 21 → 22 taken 800 times.
✗ Branch 21 → 47 not taken.
800 dotCode << " " << std::any_cast<std::string>(cstVisualizer.visit(antlrCtx.parser->entry())) << "}";
137
1/2
✓ Branch 25 → 26 taken 800 times.
✗ Branch 25 → 62 not taken.
800 antlrCtx.parser->reset();
138
139 // Dump the serialized CST string and the SVG file
140
2/4
✓ Branch 26 → 27 taken 800 times.
✗ Branch 26 → 28 not taken.
✓ Branch 27 → 28 taken 800 times.
✗ Branch 27 → 32 not taken.
800 if (cliOptions.dump.dumpCST || cliOptions.testMode)
141
1/2
✓ Branch 28 → 29 taken 800 times.
✗ Branch 28 → 53 not taken.
800 compilerOutput.cstString = dotCode.str();
142
143
1/2
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 40 taken 800 times.
800 if (cliOptions.dump.dumpCST)
144 visualizerOutput("CST", compilerOutput.cstString);
145
146 800 previousStage = CST_VISUALIZER;
147
1/2
✓ Branch 40 → 41 taken 800 times.
✗ Branch 40 → 62 not taken.
800 timer.stop();
148
1/2
✓ Branch 41 → 42 taken 800 times.
✗ Branch 41 → 60 not taken.
800 printStatusMessage("CST Visualizer", IO_CST, IO_CST, compilerOutput.times.cstVisualizer);
149 800 }
150
151 1452 void SourceFile::runASTBuilder() {
152 // Skip if restored from the cache or this stage has already been done
153
3/4
✓ Branch 2 → 3 taken 1452 times.
✗ Branch 2 → 4 not taken.
✓ Branch 3 → 4 taken 213 times.
✓ Branch 3 → 5 taken 1239 times.
1452 if (restoredFromCache || previousStage >= AST_BUILDER)
154 213 return;
155
156
1/2
✓ Branch 5 → 6 taken 1239 times.
✗ Branch 5 → 36 not taken.
1239 Timer timer(&compilerOutput.times.astBuilder);
157
1/2
✓ Branch 6 → 7 taken 1239 times.
✗ Branch 6 → 36 not taken.
1239 timer.start();
158
159 // Build AST for this source file
160
1/2
✓ Branch 8 → 9 taken 1239 times.
✗ Branch 8 → 36 not taken.
1239 ASTBuilder astBuilder(resourceManager, this, antlrCtx.inputStream.get());
161
5/6
✓ Branch 10 → 11 taken 1237 times.
✓ Branch 10 → 26 taken 2 times.
✓ Branch 11 → 12 taken 1231 times.
✓ Branch 11 → 26 taken 6 times.
✓ Branch 12 → 13 taken 1231 times.
✗ Branch 12 → 24 not taken.
1239 ast = std::any_cast<EntryNode *>(astBuilder.visit(antlrCtx.parser->entry()));
162
1/2
✓ Branch 15 → 16 taken 1231 times.
✗ Branch 15 → 34 not taken.
1231 antlrCtx.parser->reset();
163
164 // Create global scope
165
1/2
✓ Branch 16 → 17 taken 1231 times.
✗ Branch 16 → 27 not taken.
1231 globalScope = std::make_unique<Scope>(nullptr, this, ScopeType::GLOBAL, &ast->codeLoc);
166
167 1231 previousStage = AST_BUILDER;
168
1/2
✓ Branch 19 → 20 taken 1231 times.
✗ Branch 19 → 34 not taken.
1231 timer.stop();
169
1/2
✓ Branch 20 → 21 taken 1231 times.
✗ Branch 20 → 32 not taken.
1231 printStatusMessage("AST Builder", IO_CST, IO_AST, compilerOutput.times.astBuilder);
170 1239 }
171
172 1013 void SourceFile::runASTVisualizer() {
173 // Only execute if enabled
174
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 1013 times.
1013 if (restoredFromCache)
175 213 return;
176
2/4
✓ Branch 4 → 5 taken 1013 times.
✗ Branch 4 → 7 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 1013 times.
1013 if (!cliOptions.dump.dumpAST && !cliOptions.testMode)
177 return;
178 // Check if this stage has already been done
179
2/2
✓ Branch 7 → 8 taken 213 times.
✓ Branch 7 → 9 taken 800 times.
1013 if (previousStage >= AST_VISUALIZER)
180 213 return;
181
182
1/2
✓ Branch 9 → 10 taken 800 times.
✗ Branch 9 → 58 not taken.
800 Timer timer(&compilerOutput.times.astVisualizer);
183
1/2
✓ Branch 10 → 11 taken 800 times.
✗ Branch 10 → 58 not taken.
800 timer.start();
184
185 // Generate dot code for this source file
186
1/2
✓ Branch 11 → 12 taken 800 times.
✗ Branch 11 → 58 not taken.
800 std::stringstream dotCode;
187
1/2
✓ Branch 12 → 13 taken 800 times.
✗ Branch 12 → 56 not taken.
800 visualizerPreamble(dotCode);
188
1/2
✓ Branch 13 → 14 taken 800 times.
✗ Branch 13 → 56 not taken.
800 ASTVisualizer astVisualizer(resourceManager, this);
189
5/10
✓ Branch 14 → 15 taken 800 times.
✗ Branch 14 → 54 not taken.
✓ Branch 15 → 16 taken 800 times.
✗ Branch 15 → 43 not taken.
✓ Branch 16 → 17 taken 800 times.
✗ Branch 16 → 41 not taken.
✓ Branch 17 → 18 taken 800 times.
✗ Branch 17 → 39 not taken.
✓ Branch 18 → 19 taken 800 times.
✗ Branch 18 → 39 not taken.
800 dotCode << " " << std::any_cast<std::string>(astVisualizer.visit(ast)) << "}";
190
191 // Dump the serialized AST string and the SVG file
192
1/2
✓ Branch 21 → 22 taken 800 times.
✗ Branch 21 → 45 not taken.
800 compilerOutput.astString = dotCode.str();
193
194
1/2
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 32 taken 800 times.
800 if (cliOptions.dump.dumpAST)
195 visualizerOutput("AST", compilerOutput.astString);
196
197 800 previousStage = AST_VISUALIZER;
198
1/2
✓ Branch 32 → 33 taken 800 times.
✗ Branch 32 → 54 not taken.
800 timer.stop();
199
1/2
✓ Branch 33 → 34 taken 800 times.
✗ Branch 33 → 52 not taken.
800 printStatusMessage("AST Visualizer", IO_AST, IO_AST, compilerOutput.times.astVisualizer);
200 800 }
201
202 1444 void SourceFile::runImportCollector() { // NOLINT(misc-no-recursion)
203 // Skip if restored from the cache or this stage has already been done
204
3/4
✓ Branch 2 → 3 taken 1444 times.
✗ Branch 2 → 4 not taken.
✓ Branch 3 → 4 taken 213 times.
✓ Branch 3 → 5 taken 1231 times.
1444 if (restoredFromCache || previousStage >= IMPORT_COLLECTOR)
205 213 return;
206
207
1/2
✓ Branch 5 → 6 taken 1231 times.
✗ Branch 5 → 30 not taken.
1231 Timer timer(&compilerOutput.times.importCollector);
208
1/2
✓ Branch 6 → 7 taken 1231 times.
✗ Branch 6 → 30 not taken.
1231 timer.start();
209
210 // Collect the imports for this source file
211
1/2
✓ Branch 7 → 8 taken 1231 times.
✗ Branch 7 → 30 not taken.
1231 ImportCollector importCollector(resourceManager, this);
212
2/2
✓ Branch 8 → 9 taken 1226 times.
✓ Branch 8 → 24 taken 5 times.
1231 importCollector.visit(ast);
213
214 1226 previousStage = IMPORT_COLLECTOR;
215
1/2
✓ Branch 10 → 11 taken 1226 times.
✗ Branch 10 → 28 not taken.
1226 timer.stop();
216
217 // Run first part of pipeline for the imported source file
218
5/8
✓ Branch 11 → 12 taken 1226 times.
✗ Branch 11 → 25 not taken.
✓ Branch 12 → 13 taken 1226 times.
✗ Branch 12 → 25 not taken.
✓ Branch 13 → 14 taken 1226 times.
✗ Branch 13 → 25 not taken.
✓ Branch 19 → 15 taken 647 times.
✓ Branch 19 → 20 taken 1224 times.
1871 for (SourceFile *sourceFile : dependencies | std::views::values)
219
2/2
✓ Branch 16 → 17 taken 645 times.
✓ Branch 16 → 25 taken 2 times.
647 sourceFile->runFrontEnd();
220
221
1/2
✓ Branch 20 → 21 taken 1224 times.
✗ Branch 20 → 26 not taken.
1224 printStatusMessage("Import Collector", IO_AST, IO_AST, compilerOutput.times.importCollector);
222 1231 }
223
224 1437 void SourceFile::runSymbolTableBuilder() {
225 // Skip if restored from the cache or this stage has already been done
226
3/4
✓ Branch 2 → 3 taken 1437 times.
✗ Branch 2 → 4 not taken.
✓ Branch 3 → 4 taken 213 times.
✓ Branch 3 → 5 taken 1224 times.
1437 if (restoredFromCache || previousStage >= SYMBOL_TABLE_BUILDER)
227 213 return;
228
229
1/2
✓ Branch 5 → 6 taken 1224 times.
✗ Branch 5 → 30 not taken.
1224 Timer timer(&compilerOutput.times.symbolTableBuilder);
230
1/2
✓ Branch 6 → 7 taken 1224 times.
✗ Branch 6 → 30 not taken.
1224 timer.start();
231
232 // The symbol tables of all dependencies are present at this point, so we can merge the exported name registries in
233
2/2
✓ Branch 15 → 9 taken 645 times.
✓ Branch 15 → 16 taken 1224 times.
1869 for (const auto &[importName, sourceFile] : dependencies)
234
1/2
✓ Branch 12 → 13 taken 645 times.
✗ Branch 12 → 24 not taken.
645 mergeNameRegistries(*sourceFile, importName);
235
236 // Build symbol table of the current file
237
1/2
✓ Branch 16 → 17 taken 1224 times.
✗ Branch 16 → 30 not taken.
1224 SymbolTableBuilder symbolTableBuilder(resourceManager, this);
238
2/2
✓ Branch 17 → 18 taken 1206 times.
✓ Branch 17 → 25 taken 18 times.
1224 symbolTableBuilder.visit(ast);
239
240 1206 previousStage = SYMBOL_TABLE_BUILDER;
241
1/2
✓ Branch 19 → 20 taken 1206 times.
✗ Branch 19 → 28 not taken.
1206 timer.stop();
242
1/2
✓ Branch 20 → 21 taken 1206 times.
✗ Branch 20 → 26 not taken.
1206 printStatusMessage("Symbol Table Builder", IO_AST, IO_AST, compilerOutput.times.symbolTableBuilder);
243 1224 }
244
245 1418 void SourceFile::runTypeCheckerPre() { // NOLINT(misc-no-recursion)
246 // Skip if restored from the cache or this stage has already been done
247
3/4
✓ Branch 2 → 3 taken 1418 times.
✗ Branch 2 → 4 not taken.
✓ Branch 3 → 4 taken 213 times.
✓ Branch 3 → 5 taken 1205 times.
1418 if (restoredFromCache || previousStage >= TYPE_CHECKER_PRE)
248 213 return;
249
250 // Type-check all dependencies first
251
5/8
✓ Branch 5 → 6 taken 1205 times.
✗ Branch 5 → 24 not taken.
✓ Branch 6 → 7 taken 1205 times.
✗ Branch 6 → 24 not taken.
✓ Branch 7 → 8 taken 1205 times.
✗ Branch 7 → 24 not taken.
✓ Branch 13 → 9 taken 644 times.
✓ Branch 13 → 14 taken 1205 times.
1849 for (SourceFile *sourceFile : dependencies | std::views::values)
252
1/2
✓ Branch 10 → 11 taken 644 times.
✗ Branch 10 → 24 not taken.
644 sourceFile->runTypeCheckerPre();
253
254
1/2
✓ Branch 14 → 15 taken 1205 times.
✗ Branch 14 → 30 not taken.
1205 Timer timer(&compilerOutput.times.typeCheckerPre);
255
1/2
✓ Branch 15 → 16 taken 1205 times.
✗ Branch 15 → 30 not taken.
1205 timer.start();
256
257 // Then type-check the current file
258
1/2
✓ Branch 16 → 17 taken 1205 times.
✗ Branch 16 → 30 not taken.
1205 TypeChecker typeChecker(resourceManager, this, TC_MODE_PRE);
259
2/2
✓ Branch 17 → 18 taken 1191 times.
✓ Branch 17 → 25 taken 14 times.
1205 typeChecker.visit(ast);
260
261 1191 previousStage = TYPE_CHECKER_PRE;
262
1/2
✓ Branch 19 → 20 taken 1191 times.
✗ Branch 19 → 28 not taken.
1191 timer.stop();
263
1/2
✓ Branch 20 → 21 taken 1191 times.
✗ Branch 20 → 26 not taken.
1191 printStatusMessage("Type Checker Pre", IO_AST, IO_AST, compilerOutput.times.typeCheckerPre);
264 1205 }
265
266 2971 void SourceFile::runTypeCheckerPost() { // NOLINT(misc-no-recursion)
267 // Skip if restored from cache, this stage has already been done, or not all dependants finished type checking
268
6/8
✓ Branch 2 → 3 taken 2971 times.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 2971 times.
✗ Branch 3 → 80 not taken.
✓ Branch 4 → 5 taken 544 times.
✓ Branch 4 → 6 taken 2427 times.
✓ Branch 7 → 8 taken 544 times.
✓ Branch 7 → 9 taken 2427 times.
2971 if (restoredFromCache || !haveAllDependantsBeenTypeChecked())
269 544 return;
270
271
1/2
✓ Branch 9 → 10 taken 2427 times.
✗ Branch 9 → 80 not taken.
2427 Timer timer(&compilerOutput.times.typeCheckerPost);
272
1/2
✓ Branch 10 → 11 taken 2427 times.
✗ Branch 10 → 80 not taken.
2427 timer.start();
273
274 // Start type-checking loop. The type-checker can request a re-execution. The max number of type-checker runs is limited
275
1/2
✓ Branch 11 → 12 taken 2427 times.
✗ Branch 11 → 80 not taken.
2427 TypeChecker typeChecker(resourceManager, this, TC_MODE_POST);
276 2427 unsigned short typeCheckerRuns = 0;
277
2/2
✓ Branch 27 → 13 taken 1660 times.
✓ Branch 27 → 28 taken 2364 times.
4024 while (reVisitRequested) {
278 1660 typeCheckerRuns++;
279 1660 totalTypeCheckerRuns++;
280 1660 reVisitRequested = false;
281
282 // Type-check the current file first. Multiple times, if requested
283 1660 timer.resume();
284
2/2
✓ Branch 14 → 15 taken 1628 times.
✓ Branch 14 → 58 taken 32 times.
1660 typeChecker.visit(ast);
285
1/2
✓ Branch 16 → 17 taken 1628 times.
✗ Branch 16 → 78 not taken.
1628 timer.pause();
286
287 // Then type-check all dependencies
288
5/8
✓ Branch 17 → 18 taken 1628 times.
✗ Branch 17 → 59 not taken.
✓ Branch 18 → 19 taken 1628 times.
✗ Branch 18 → 59 not taken.
✓ Branch 19 → 20 taken 1628 times.
✗ Branch 19 → 59 not taken.
✓ Branch 25 → 21 taken 2569 times.
✓ Branch 25 → 26 taken 1597 times.
4166 for (SourceFile *sourceFile : dependencies | std::views::values)
289
2/2
✓ Branch 22 → 23 taken 2538 times.
✓ Branch 22 → 59 taken 31 times.
2569 sourceFile->runTypeCheckerPost();
290 }
291
292
2/2
✓ Branch 28 → 29 taken 2259 times.
✓ Branch 28 → 78 taken 105 times.
2364 checkForSoftErrors();
293
294 // Check if all dyn variables were type-inferred successfully
295
2/2
✓ Branch 30 → 31 taken 2258 times.
✓ Branch 30 → 78 taken 1 time.
2259 globalScope->ensureSuccessfulTypeInference();
296
297 2258 previousStage = TYPE_CHECKER_POST;
298
1/2
✓ Branch 31 → 32 taken 2258 times.
✗ Branch 31 → 78 not taken.
2258 timer.stop();
299
1/2
✓ Branch 32 → 33 taken 2258 times.
✗ Branch 32 → 60 not taken.
2258 printStatusMessage("Type Checker Post", IO_AST, IO_AST, compilerOutput.times.typeCheckerPost, typeCheckerRuns);
300
301 // Save the JSON version in the compiler output
302
2/4
✓ Branch 33 → 34 taken 2258 times.
✗ Branch 33 → 35 not taken.
✓ Branch 34 → 35 taken 2258 times.
✗ Branch 34 → 42 not taken.
2258 if (cliOptions.dump.dumpSymbolTable || cliOptions.testMode)
303
2/4
✓ Branch 36 → 37 taken 2258 times.
✗ Branch 36 → 64 not taken.
✓ Branch 37 → 38 taken 2258 times.
✗ Branch 37 → 62 not taken.
2258 compilerOutput.symbolTableString = globalScope->getSymbolTableJSON().dump(/*indent=*/2);
304
305 // Dump symbol table
306
1/2
✗ Branch 42 → 43 not taken.
✓ Branch 42 → 55 taken 2258 times.
2258 if (cliOptions.dump.dumpSymbolTable)
307 dumpOutput(compilerOutput.symbolTableString, "Symbol Table", "symbol-table.json");
308 2427 }
309
310 266 void SourceFile::runDependencyGraphVisualizer() {
311 // Only execute if enabled
312
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 266 times.
266 if (restoredFromCache)
313 2 return;
314
2/4
✓ Branch 4 → 5 taken 266 times.
✗ Branch 4 → 7 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 266 times.
266 if (!cliOptions.dump.dumpDependencyGraph && !cliOptions.testMode)
315 return;
316 // Check if this stage has already been done
317
2/2
✓ Branch 7 → 8 taken 2 times.
✓ Branch 7 → 9 taken 264 times.
266 if (previousStage >= DEP_GRAPH_VISUALIZER)
318 2 return;
319
320
1/2
✓ Branch 9 → 10 taken 264 times.
✗ Branch 9 → 47 not taken.
264 Timer timer(&compilerOutput.times.depGraphVisualizer);
321
1/2
✓ Branch 10 → 11 taken 264 times.
✗ Branch 10 → 47 not taken.
264 timer.start();
322
323 // Generate dot code for this source file
324
1/2
✓ Branch 11 → 12 taken 264 times.
✗ Branch 11 → 47 not taken.
264 std::stringstream dotCode;
325
1/2
✓ Branch 12 → 13 taken 264 times.
✗ Branch 12 → 45 not taken.
264 visualizerPreamble(dotCode);
326
1/2
✓ Branch 13 → 14 taken 264 times.
✗ Branch 13 → 45 not taken.
264 DependencyGraphVisualizer depGraphVisualizer(resourceManager, this);
327
1/2
✓ Branch 14 → 15 taken 264 times.
✗ Branch 14 → 43 not taken.
264 depGraphVisualizer.getDependencyGraph(dotCode);
328
1/2
✓ Branch 15 → 16 taken 264 times.
✗ Branch 15 → 43 not taken.
264 dotCode << "}";
329
330 // Dump the serialized AST string and the SVG file
331
1/2
✓ Branch 16 → 17 taken 264 times.
✗ Branch 16 → 34 not taken.
264 compilerOutput.depGraphString = dotCode.str();
332
333
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 27 taken 264 times.
264 if (cliOptions.dump.dumpDependencyGraph)
334 visualizerOutput("Dependency Graph", compilerOutput.depGraphString);
335
336 264 previousStage = DEP_GRAPH_VISUALIZER;
337
1/2
✓ Branch 27 → 28 taken 264 times.
✗ Branch 27 → 43 not taken.
264 timer.stop();
338
1/2
✓ Branch 28 → 29 taken 264 times.
✗ Branch 28 → 41 not taken.
264 printStatusMessage("AST Visualizer", IO_AST, IO_AST, compilerOutput.times.depGraphVisualizer);
339 264 }
340
341 3626 void SourceFile::runIRGenerator() {
342 // Skip if restored from the cache or this stage has already been done
343
3/4
✓ Branch 2 → 3 taken 3626 times.
✗ Branch 2 → 4 not taken.
✓ Branch 3 → 4 taken 2628 times.
✓ Branch 3 → 5 taken 998 times.
3626 if (restoredFromCache || previousStage >= IR_GENERATOR)
344 2628 return;
345
346
1/2
✓ Branch 5 → 6 taken 998 times.
✗ Branch 5 → 76 not taken.
998 Timer timer(&compilerOutput.times.irGenerator);
347
1/2
✓ Branch 6 → 7 taken 998 times.
✗ Branch 6 → 76 not taken.
998 timer.start();
348
349 // Create the LLVM module for this source file
350
2/2
✓ Branch 7 → 8 taken 2 times.
✓ Branch 7 → 9 taken 996 times.
998 llvm::LLVMContext &llvmContext = cliOptions.useLTO ? resourceManager.ltoContext : context;
351
1/2
✓ Branch 10 → 11 taken 998 times.
✗ Branch 10 → 50 not taken.
998 llvmModule = std::make_unique<llvm::Module>(fileName, llvmContext);
352
353 // Generate this source file
354
1/2
✓ Branch 13 → 14 taken 998 times.
✗ Branch 13 → 76 not taken.
998 IRGenerator irGenerator(resourceManager, this);
355
1/2
✓ Branch 14 → 15 taken 998 times.
✗ Branch 14 → 51 not taken.
998 irGenerator.visit(ast);
356
357 // Print warning if the verifier is disabled
358
4/4
✓ Branch 16 → 17 taken 264 times.
✓ Branch 16 → 25 taken 734 times.
✓ Branch 17 → 18 taken 1 time.
✓ Branch 17 → 25 taken 263 times.
998 if (isMainFile && cliOptions.disableVerifier) {
359
1/2
✓ Branch 20 → 21 taken 1 time.
✗ Branch 20 → 52 not taken.
1 const std::string warningMessage = "The LLVM verifier passes are disabled. Please use this cli option with caution.";
360
1/2
✓ Branch 22 → 23 taken 1 time.
✗ Branch 22 → 55 not taken.
1 compilerOutput.warnings.emplace_back(VERIFIER_DISABLED, warningMessage);
361 1 }
362
363 // Save the ir string in the compiler output
364
2/4
✓ Branch 25 → 26 taken 998 times.
✗ Branch 25 → 27 not taken.
✓ Branch 26 → 27 taken 998 times.
✗ Branch 26 → 32 not taken.
998 if (cliOptions.dump.dumpIR || cliOptions.testMode)
365
1/2
✓ Branch 28 → 29 taken 998 times.
✗ Branch 28 → 59 not taken.
998 compilerOutput.irString = IRGenerator::getIRString(llvmModule.get(), cliOptions);
366
367 // Dump unoptimized IR code
368
1/2
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 45 taken 998 times.
998 if (cliOptions.dump.dumpIR)
369 dumpOutput(compilerOutput.irString, "Unoptimized IR Code", "ir-code.ll");
370
371 998 previousStage = IR_GENERATOR;
372
1/2
✓ Branch 45 → 46 taken 998 times.
✗ Branch 45 → 74 not taken.
998 timer.stop();
373
1/2
✓ Branch 46 → 47 taken 998 times.
✗ Branch 46 → 72 not taken.
998 printStatusMessage("IR Generator", IO_AST, IO_IR, compilerOutput.times.irGenerator);
374 998 }
375
376 3542 void SourceFile::runDefaultIROptimizer() {
377
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 3542 times.
3542 assert(!cliOptions.useLTO);
378
379 // Skip if restored from the cache or this stage has already been done
380
6/8
✓ Branch 4 → 5 taken 3542 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 914 times.
✓ Branch 5 → 8 taken 2628 times.
✓ Branch 6 → 7 taken 26 times.
✓ Branch 6 → 9 taken 888 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 26 times.
3542 if (restoredFromCache || previousStage > IR_OPTIMIZER || (previousStage == IR_OPTIMIZER && !cliOptions.testMode))
381 2628 return;
382
383
1/2
✓ Branch 9 → 10 taken 914 times.
✗ Branch 9 → 60 not taken.
914 Timer timer(&compilerOutput.times.irOptimizer);
384
1/2
✓ Branch 10 → 11 taken 914 times.
✗ Branch 10 → 60 not taken.
914 timer.start();
385
386 // Optimize this source file
387
1/2
✓ Branch 11 → 12 taken 914 times.
✗ Branch 11 → 60 not taken.
914 IROptimizer irOptimizer(resourceManager, this);
388
1/2
✓ Branch 12 → 13 taken 914 times.
✗ Branch 12 → 58 not taken.
914 irOptimizer.prepare();
389
1/2
✓ Branch 13 → 14 taken 914 times.
✗ Branch 13 → 58 not taken.
914 irOptimizer.optimizeDefault();
390
391 // Save the optimized ir string in the compiler output
392
2/4
✓ Branch 14 → 15 taken 914 times.
✗ Branch 14 → 16 not taken.
✓ Branch 15 → 16 taken 914 times.
✗ Branch 15 → 21 not taken.
914 if (cliOptions.dump.dumpIR || cliOptions.testMode)
393
1/2
✓ Branch 17 → 18 taken 914 times.
✗ Branch 17 → 40 not taken.
914 compilerOutput.irOptString = IRGenerator::getIRString(llvmModule.get(), cliOptions);
394
395 // Dump optimized IR code
396
1/2
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 35 taken 914 times.
914 if (cliOptions.dump.dumpIR)
397 dumpOutput(compilerOutput.irOptString, "Optimized IR Code",
398 "ir-code-O" + std::to_string(static_cast<uint8_t>(cliOptions.optLevel)) + ".ll");
399
400 914 previousStage = IR_OPTIMIZER;
401
1/2
✓ Branch 35 → 36 taken 914 times.
✗ Branch 35 → 58 not taken.
914 timer.stop();
402
1/2
✓ Branch 36 → 37 taken 914 times.
✗ Branch 36 → 56 not taken.
914 printStatusMessage("IR Optimizer", IO_IR, IO_IR, compilerOutput.times.irOptimizer);
403 914 }
404
405 2 void SourceFile::runPreLinkIROptimizer() {
406
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2 times.
2 assert(cliOptions.useLTO);
407
408 // Skip if restored from the cache or this stage has already been done
409
2/4
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 6 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 2 times.
2 if (restoredFromCache || previousStage >= IR_OPTIMIZER)
410 return;
411
412
1/2
✓ Branch 7 → 8 taken 2 times.
✗ Branch 7 → 51 not taken.
2 Timer timer(&compilerOutput.times.irOptimizer);
413
1/2
✓ Branch 8 → 9 taken 2 times.
✗ Branch 8 → 51 not taken.
2 timer.start();
414
415 // Optimize this source file
416
1/2
✓ Branch 9 → 10 taken 2 times.
✗ Branch 9 → 51 not taken.
2 IROptimizer irOptimizer(resourceManager, this);
417
1/2
✓ Branch 10 → 11 taken 2 times.
✗ Branch 10 → 49 not taken.
2 irOptimizer.prepare();
418
1/2
✓ Branch 11 → 12 taken 2 times.
✗ Branch 11 → 49 not taken.
2 irOptimizer.optimizePreLink();
419
420 // Save the optimized ir string in the compiler output
421
2/4
✓ Branch 12 → 13 taken 2 times.
✗ Branch 12 → 14 not taken.
✓ Branch 13 → 14 taken 2 times.
✗ Branch 13 → 19 not taken.
2 if (cliOptions.dump.dumpIR || cliOptions.testMode)
422
1/2
✓ Branch 15 → 16 taken 2 times.
✗ Branch 15 → 36 not taken.
2 compilerOutput.irOptString = IRGenerator::getIRString(llvmModule.get(), cliOptions);
423
424 // Dump optimized IR code
425
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 32 taken 2 times.
2 if (cliOptions.dump.dumpIR)
426 dumpOutput(compilerOutput.irOptString, "Optimized IR Code (pre-link)", "ir-code-lto-pre-link.ll");
427
428
1/2
✓ Branch 32 → 33 taken 2 times.
✗ Branch 32 → 49 not taken.
2 timer.pause();
429 2 }
430
431 2 void SourceFile::runBitcodeLinker() {
432
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2 times.
2 assert(cliOptions.useLTO);
433
434 // Skip if this is not the main source file
435
2/2
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 6 taken 1 time.
2 if (!isMainFile)
436 1 return;
437
438 // Skip if restored from the cache or this stage has already been done
439
2/4
✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 8 not taken.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 1 time.
1 if (restoredFromCache || previousStage >= IR_OPTIMIZER)
440 return;
441
442
1/2
✓ Branch 9 → 10 taken 1 time.
✗ Branch 9 → 20 not taken.
1 Timer timer(&compilerOutput.times.irOptimizer);
443 1 timer.resume();
444
445 // Link all source files together
446
1/2
✓ Branch 11 → 12 taken 1 time.
✗ Branch 11 → 20 not taken.
1 BitcodeLinker linker(resourceManager);
447
1/2
✓ Branch 12 → 13 taken 1 time.
✗ Branch 12 → 18 not taken.
1 linker.link();
448
449
1/2
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 18 not taken.
1 timer.pause();
450 1 }
451
452 2 void SourceFile::runPostLinkIROptimizer() {
453
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2 times.
2 assert(cliOptions.useLTO);
454
455 // Skip if this is not the main source file
456
2/2
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 6 taken 1 time.
2 if (!isMainFile)
457 1 return;
458
459 // Skip if restored from the cache or this stage has already been done
460
2/4
✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 8 not taken.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 1 time.
1 if (restoredFromCache || previousStage >= IR_OPTIMIZER)
461 return;
462
463
1/2
✓ Branch 9 → 10 taken 1 time.
✗ Branch 9 → 57 not taken.
1 Timer timer(&compilerOutput.times.irOptimizer);
464 1 timer.resume();
465
466 // Optimize LTO module
467
1/2
✓ Branch 11 → 12 taken 1 time.
✗ Branch 11 → 57 not taken.
1 IROptimizer irOptimizer(resourceManager, this);
468
1/2
✓ Branch 12 → 13 taken 1 time.
✗ Branch 12 → 55 not taken.
1 irOptimizer.prepare();
469
1/2
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 55 not taken.
1 irOptimizer.optimizePostLink();
470
471 // Save the optimized ir string in the compiler output
472
2/4
✓ Branch 14 → 15 taken 1 time.
✗ Branch 14 → 16 not taken.
✓ Branch 15 → 16 taken 1 time.
✗ Branch 15 → 21 not taken.
1 if (cliOptions.dump.dumpIR || cliOptions.testMode) {
473 1 llvm::Module *module = resourceManager.ltoModule.get();
474
1/2
✓ Branch 17 → 18 taken 1 time.
✗ Branch 17 → 40 not taken.
1 compilerOutput.irOptString = IRGenerator::getIRString(module, cliOptions);
475 }
476
477 // Dump optimized IR code
478
1/2
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 34 taken 1 time.
1 if (cliOptions.dump.dumpIR)
479 dumpOutput(compilerOutput.irOptString, "Optimized IR Code (post-Link)", "ir-code-lto-post-link.ll");
480
481 1 previousStage = IR_OPTIMIZER;
482
1/2
✓ Branch 34 → 35 taken 1 time.
✗ Branch 34 → 55 not taken.
1 timer.stop();
483
1/2
✓ Branch 35 → 36 taken 1 time.
✗ Branch 35 → 53 not taken.
1 printStatusMessage("IR Optimizer", IO_IR, IO_IR, compilerOutput.times.irOptimizer);
484 1 }
485
486 3567 void SourceFile::runObjectEmitter() {
487 // Skip if restored from the cache or this stage has already been done
488
3/4
✓ Branch 2 → 3 taken 3567 times.
✗ Branch 2 → 4 not taken.
✓ Branch 3 → 4 taken 2628 times.
✓ Branch 3 → 5 taken 939 times.
3567 if (restoredFromCache || previousStage >= OBJECT_EMITTER)
489 2629 return;
490
491 // Skip if LTO is enabled and this is not the main source file
492
4/4
✓ Branch 5 → 6 taken 2 times.
✓ Branch 5 → 8 taken 937 times.
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 8 taken 1 time.
939 if (cliOptions.useLTO && !isMainFile)
493 1 return;
494
495
1/2
✓ Branch 8 → 9 taken 938 times.
✗ Branch 8 → 67 not taken.
938 Timer timer(&compilerOutput.times.objectEmitter);
496
1/2
✓ Branch 9 → 10 taken 938 times.
✗ Branch 9 → 67 not taken.
938 timer.start();
497
498 // Deduce an object file path
499
2/4
✓ Branch 10 → 11 taken 938 times.
✗ Branch 10 → 45 not taken.
✓ Branch 11 → 12 taken 938 times.
✗ Branch 11 → 43 not taken.
938 std::filesystem::path objectFilePath = cliOptions.outputDir / filePath.filename();
500
2/4
✓ Branch 13 → 14 taken 938 times.
✗ Branch 13 → 48 not taken.
✓ Branch 14 → 15 taken 938 times.
✗ Branch 14 → 46 not taken.
938 objectFilePath.replace_extension("o");
501
502 // Emit object for this source file
503
1/2
✓ Branch 16 → 17 taken 938 times.
✗ Branch 16 → 65 not taken.
938 const ObjectEmitter objectEmitter(resourceManager, this);
504
1/2
✓ Branch 17 → 18 taken 938 times.
✗ Branch 17 → 63 not taken.
938 objectEmitter.emit(objectFilePath);
505
506 // Save assembly string in the compiler output
507
4/6
✓ Branch 18 → 19 taken 894 times.
✓ Branch 18 → 22 taken 44 times.
✓ Branch 19 → 20 taken 894 times.
✗ Branch 19 → 21 not taken.
✓ Branch 20 → 21 taken 894 times.
✗ Branch 20 → 22 not taken.
938 if (cliOptions.isNativeTarget && (cliOptions.dump.dumpAssembly || cliOptions.testMode))
508
1/2
✓ Branch 21 → 22 taken 894 times.
✗ Branch 21 → 63 not taken.
894 objectEmitter.getASMString(compilerOutput.asmString);
509
510 // Dump assembly code
511
1/2
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 35 taken 938 times.
938 if (cliOptions.dump.dumpAssembly)
512 dumpOutput(compilerOutput.asmString, "Assembly code", "assembly-code.s");
513
514 // Add the object file to the linker objects
515
1/2
✓ Branch 35 → 36 taken 938 times.
✗ Branch 35 → 63 not taken.
938 resourceManager.linker.addObjectFilePath(objectFilePath);
516
517 938 previousStage = OBJECT_EMITTER;
518
1/2
✓ Branch 36 → 37 taken 938 times.
✗ Branch 36 → 63 not taken.
938 timer.stop();
519
1/2
✓ Branch 37 → 38 taken 938 times.
✗ Branch 37 → 61 not taken.
938 printStatusMessage("Object Emitter", IO_IR, IO_OBJECT_FILE, compilerOutput.times.objectEmitter);
520 938 }
521
522 3567 void SourceFile::concludeCompilation() {
523 // Skip if restored from the cache or this stage has already been done
524
3/4
✓ Branch 2 → 3 taken 3567 times.
✗ Branch 2 → 4 not taken.
✓ Branch 3 → 4 taken 2628 times.
✓ Branch 3 → 5 taken 939 times.
3567 if (restoredFromCache || previousStage >= FINISHED)
525 2628 return;
526
527 // Cache the source file
528
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 939 times.
939 if (!cliOptions.ignoreCache)
529 resourceManager.cacheManager.cacheSourceFile(this);
530
531 // Save type registry as string in the compiler output
532
4/6
✓ Branch 7 → 8 taken 205 times.
✓ Branch 7 → 14 taken 734 times.
✓ Branch 8 → 9 taken 205 times.
✗ Branch 8 → 10 not taken.
✓ Branch 9 → 10 taken 205 times.
✗ Branch 9 → 14 not taken.
939 if (isMainFile && (cliOptions.dump.dumpTypes || cliOptions.testMode))
533
1/2
✓ Branch 10 → 11 taken 205 times.
✗ Branch 10 → 52 not taken.
205 compilerOutput.typesString = TypeRegistry::dump();
534
535 // Dump type registry
536
3/4
✓ Branch 14 → 15 taken 205 times.
✓ Branch 14 → 28 taken 734 times.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 28 taken 205 times.
939 if (isMainFile && cliOptions.dump.dumpTypes)
537 dumpOutput(compilerOutput.typesString, "Type Registry", "type-registry.out");
538
539 // Save cache statistics as string in the compiler output
540
4/6
✓ Branch 28 → 29 taken 205 times.
✓ Branch 28 → 32 taken 734 times.
✓ Branch 29 → 30 taken 205 times.
✗ Branch 29 → 31 not taken.
✓ Branch 30 → 31 taken 205 times.
✗ Branch 30 → 32 not taken.
939 if (isMainFile && (cliOptions.dump.dumpCacheStats || cliOptions.testMode))
541 205 dumpCacheStats();
542
543 // Dump lookup cache statistics
544
3/4
✓ Branch 32 → 33 taken 205 times.
✓ Branch 32 → 46 taken 734 times.
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 46 taken 205 times.
939 if (isMainFile && cliOptions.dump.dumpCacheStats)
545 dumpOutput(compilerOutput.cacheStats, "Cache Statistics", "cache-stats.out");
546
547
1/2
✗ Branch 46 → 47 not taken.
✓ Branch 46 → 50 taken 939 times.
939 if (cliOptions.printDebugOutput)
548 std::cout << "Finished compiling " << fileName << std::endl;
549
550 939 previousStage = FINISHED;
551 }
552
553 1005 void SourceFile::runFrontEnd() { // NOLINT(misc-no-recursion)
554 1005 runLexer();
555
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1005 times.
1005 CHECK_ABORT_FLAG_V()
556 1005 runParser();
557
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 1005 times.
1005 CHECK_ABORT_FLAG_V()
558 1005 runCSTVisualizer();
559
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 1005 times.
1005 CHECK_ABORT_FLAG_V()
560 1005 runASTBuilder();
561
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 1005 times.
1005 CHECK_ABORT_FLAG_V()
562 1005 runASTVisualizer();
563
1/2
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 1005 times.
1005 CHECK_ABORT_FLAG_V()
564 1005 runImportCollector();
565
1/2
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 1003 times.
1003 CHECK_ABORT_FLAG_V()
566 1003 runSymbolTableBuilder();
567
1/2
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 1003 times.
1003 CHECK_ABORT_FLAG_V()
568 }
569
570 416 void SourceFile::runMiddleEnd() {
571 // We need two runs here due to generics.
572 // The first run to determine all concrete function/struct/interface substantiations
573 416 runTypeCheckerPre(); // Visit the dependency tree from bottom to top
574
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 402 times.
402 CHECK_ABORT_FLAG_V()
575 // The second run to ensure, also generic scopes are type-checked properly
576 402 runTypeCheckerPost(); // Visit the dependency tree from top to bottom in topological order
577
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 264 times.
264 CHECK_ABORT_FLAG_V()
578 // Visualize dependency graph
579 264 runDependencyGraphVisualizer();
580
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 264 times.
264 CHECK_ABORT_FLAG_V()
581 }
582
583 3362 void SourceFile::runBackEnd() { // NOLINT(misc-no-recursion)
584 // Run backend for all dependencies first
585
5/8
✓ Branch 2 → 3 taken 3362 times.
✗ Branch 2 → 36 not taken.
✓ Branch 3 → 4 taken 3362 times.
✗ Branch 3 → 36 not taken.
✓ Branch 4 → 5 taken 3362 times.
✗ Branch 4 → 36 not taken.
✓ Branch 10 → 6 taken 3160 times.
✓ Branch 10 → 11 taken 3362 times.
6522 for (SourceFile *sourceFile : dependencies | std::views::values)
586
1/2
✓ Branch 7 → 8 taken 3160 times.
✗ Branch 7 → 36 not taken.
3160 sourceFile->runBackEnd();
587
588 3362 runIRGenerator();
589
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 3362 times.
3362 CHECK_ABORT_FLAG_V()
590
2/2
✓ Branch 14 → 15 taken 1 time.
✓ Branch 14 → 24 taken 3361 times.
3362 if (cliOptions.useLTO) {
591 1 runPreLinkIROptimizer();
592
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 1 time.
1 CHECK_ABORT_FLAG_V()
593 1 runBitcodeLinker();
594
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 1 time.
1 CHECK_ABORT_FLAG_V()
595 1 runPostLinkIROptimizer();
596
1/2
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 27 taken 1 time.
1 CHECK_ABORT_FLAG_V()
597 } else {
598 3361 runDefaultIROptimizer();
599
1/2
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 3361 times.
3361 CHECK_ABORT_FLAG_V()
600 }
601 3362 runObjectEmitter();
602
1/2
✗ Branch 28 → 29 not taken.
✓ Branch 28 → 30 taken 3362 times.
3362 CHECK_ABORT_FLAG_V()
603 3362 concludeCompilation();
604
605
1/2
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 35 taken 3362 times.
3362 if (isMainFile) {
606 resourceManager.totalTimer.stop();
607 if (cliOptions.printDebugOutput)
608 dumpCompilationStats();
609 }
610 }
611
612 1462 void SourceFile::addDependency(SourceFile *sourceFile, const ASTNode *declNode, const std::string &dependencyName,
613 const std::string &path) {
614 // Check if this would cause a circular dependency
615
1/2
✓ Branch 2 → 3 taken 1462 times.
✗ Branch 2 → 41 not taken.
1462 std::stack<const SourceFile *> dependencyCircle;
616
3/4
✓ Branch 3 → 4 taken 1462 times.
✗ Branch 3 → 39 not taken.
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 17 taken 1461 times.
1462 if (isAlreadyImported(path, dependencyCircle)) {
617 // Build the error message
618
1/2
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 34 not taken.
1 std::stringstream errorMessage;
619
3/6
✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 32 not taken.
✓ Branch 7 → 8 taken 1 time.
✗ Branch 7 → 32 not taken.
✓ Branch 8 → 9 taken 1 time.
✗ Branch 8 → 32 not taken.
1 errorMessage << "Circular import detected while importing '" << sourceFile->fileName << "':\n\n";
620
2/4
✓ Branch 9 → 10 taken 1 time.
✗ Branch 9 → 25 not taken.
✓ Branch 10 → 11 taken 1 time.
✗ Branch 10 → 23 not taken.
1 errorMessage << CommonUtil::getCircularImportMessage(dependencyCircle);
621
2/4
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 29 not taken.
✓ Branch 14 → 15 taken 1 time.
✗ Branch 14 → 26 not taken.
1 throw SemanticError(declNode, CIRCULAR_DEPENDENCY, errorMessage.str());
622 1 }
623
624 // Add the dependency
625 1461 sourceFile->isMainFile = false;
626
2/4
✓ Branch 17 → 18 taken 1461 times.
✗ Branch 17 → 37 not taken.
✓ Branch 18 → 19 taken 1461 times.
✗ Branch 18 → 35 not taken.
1461 dependencies.insert({dependencyName, sourceFile});
627
628 // Add the dependant
629
1/2
✓ Branch 20 → 21 taken 1461 times.
✗ Branch 20 → 38 not taken.
1461 sourceFile->dependants.push_back(this);
630 1462 }
631
632 101768 bool SourceFile::imports(const SourceFile *sourceFile) const {
633 313810 return std::ranges::any_of(dependencies, [=](const auto &dependency) { return dependency.second == sourceFile; });
634 }
635
636 6731 bool SourceFile::isAlreadyImported(const std::string &filePathSearch, // NOLINT(misc-no-recursion)
637 std::stack<const SourceFile *> &circle) const {
638
1/2
✓ Branch 2 → 3 taken 6731 times.
✗ Branch 2 → 20 not taken.
6731 circle.push(this);
639
640 // Check if the current source file corresponds to the path to search
641
4/6
✓ Branch 3 → 4 taken 6731 times.
✗ Branch 3 → 23 not taken.
✓ Branch 4 → 5 taken 6731 times.
✗ Branch 4 → 21 not taken.
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 8 taken 6730 times.
6731 if (std::filesystem::equivalent(filePath, filePathSearch))
642 1 return true;
643
644 // Check dependants recursively
645
2/2
✓ Branch 16 → 10 taken 5269 times.
✓ Branch 16 → 17 taken 6728 times.
11997 for (const SourceFile *dependant : dependants)
646
3/4
✓ Branch 11 → 12 taken 5269 times.
✗ Branch 11 → 24 not taken.
✓ Branch 12 → 13 taken 2 times.
✓ Branch 12 → 14 taken 5267 times.
5269 if (dependant->isAlreadyImported(filePathSearch, circle))
647 2 return true;
648
649 // If no dependant was found, remove the current source file from the circle to continue with the next sibling
650 6728 circle.pop();
651 6728 return false;
652 }
653
654 3675 SourceFile *SourceFile::requestRuntimeModule(RuntimeModule runtimeModule) {
655 // Check if the module was already imported
656
2/2
✓ Branch 3 → 4 taken 2863 times.
✓ Branch 3 → 6 taken 812 times.
3675 if (isRuntimeModuleAvailable(runtimeModule))
657 2863 return resourceManager.runtimeModuleManager.getModule(runtimeModule);
658 812 return resourceManager.runtimeModuleManager.requestModule(this, runtimeModule);
659 }
660
661 4021 bool SourceFile::isRuntimeModuleAvailable(RuntimeModule runtimeModule) const { return importedRuntimeModules & runtimeModule; }
662
663 37887 void SourceFile::addNameRegistryEntry(const std::string &symbolName, uint64_t typeId, SymbolTableEntry *entry, Scope *scope,
664 bool keepNewOnCollision, SymbolTableEntry *importEntry) {
665
6/6
✓ Branch 2 → 3 taken 9157 times.
✓ Branch 2 → 5 taken 28730 times.
✓ Branch 4 → 5 taken 9086 times.
✓ Branch 4 → 6 taken 71 times.
✓ Branch 7 → 8 taken 37816 times.
✓ Branch 7 → 13 taken 71 times.
37887 if (keepNewOnCollision || !exportedNameRegistry.contains(symbolName)) // Overwrite potential existing entry
666 37816 exportedNameRegistry[symbolName] = {symbolName, typeId, entry, scope, importEntry};
667 else // Name collision => we must remove the existing entry
668 71 exportedNameRegistry.erase(symbolName);
669
2/6
✓ Branch 8 → 9 taken 37816 times.
✗ Branch 8 → 20 not taken.
✓ Branch 9 → 10 taken 37816 times.
✗ Branch 9 → 15 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 17 → 19 not taken.
75703 }
670
671 214459 const NameRegistryEntry *SourceFile::getNameRegistryEntry(const std::string &symbolName) const {
672
2/2
✓ Branch 3 → 4 taken 107962 times.
✓ Branch 3 → 5 taken 106497 times.
214459 if (!exportedNameRegistry.contains(symbolName))
673 107962 return nullptr;
674
675 // Resolve registry entry for the given name
676
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 106497 times.
106497 assert(exportedNameRegistry.contains(symbolName));
677 106497 const NameRegistryEntry *entry = &exportedNameRegistry.at(symbolName);
678
679 // Mark the import entry as used
680
2/2
✓ Branch 9 → 10 taken 5358 times.
✓ Branch 9 → 11 taken 101139 times.
106497 if (entry->importEntry != nullptr)
681 5358 entry->importEntry->used = true;
682
683 106497 return entry;
684 }
685
686 365030 llvm::Type *SourceFile::getLLVMType(const Type *type) {
687 // Check if the type is already in the mapping
688
1/2
✓ Branch 2 → 3 taken 365030 times.
✗ Branch 2 → 13 not taken.
365030 const auto it = typeToLLVMTypeMapping.find(type);
689
2/2
✓ Branch 5 → 6 taken 356532 times.
✓ Branch 5 → 8 taken 8498 times.
365030 if (it != typeToLLVMTypeMapping.end())
690 356532 return it->second;
691
692 // If not, generate the LLVM type
693
1/2
✓ Branch 8 → 9 taken 8498 times.
✗ Branch 8 → 13 not taken.
8498 llvm::Type *llvmType = type->toLLVMType(this);
694
1/2
✓ Branch 9 → 10 taken 8498 times.
✗ Branch 9 → 13 not taken.
8498 typeToLLVMTypeMapping[type] = llvmType;
695 8498 return llvmType;
696 }
697
698 2366 void SourceFile::checkForSoftErrors() const {
699 // Check if there are any soft errors and if so, print them
700
2/2
✓ Branch 3 → 4 taken 107 times.
✓ Branch 3 → 19 taken 2259 times.
2366 if (!resourceManager.errorManager.softErrors.empty()) {
701
1/2
✓ Branch 4 → 5 taken 107 times.
✗ Branch 4 → 29 not taken.
107 std::stringstream errorStream;
702
1/2
✓ Branch 5 → 6 taken 107 times.
✗ Branch 5 → 27 not taken.
107 errorStream << "There are unresolved errors. Please fix them and recompile.";
703
2/2
✓ Branch 13 → 8 taken 129 times.
✓ Branch 13 → 14 taken 107 times.
236 for (const auto &[codeLoc, message] : resourceManager.errorManager.softErrors)
704
2/4
✓ Branch 9 → 10 taken 129 times.
✗ Branch 9 → 20 not taken.
✓ Branch 10 → 11 taken 129 times.
✗ Branch 10 → 20 not taken.
129 errorStream << "\n\n" << message;
705
2/4
✓ Branch 15 → 16 taken 107 times.
✗ Branch 15 → 24 not taken.
✓ Branch 16 → 17 taken 107 times.
✗ Branch 16 → 21 not taken.
107 throw CompilerError(UNRESOLVED_SOFT_ERRORS, errorStream.str());
706 107 }
707 2259 }
708
709 298 void SourceFile::collectAndPrintWarnings() { // NOLINT(misc-no-recursion)
710 // Print warnings for all dependencies
711
5/8
✓ Branch 2 → 3 taken 298 times.
✗ Branch 2 → 23 not taken.
✓ Branch 3 → 4 taken 298 times.
✗ Branch 3 → 23 not taken.
✓ Branch 4 → 5 taken 298 times.
✗ Branch 4 → 23 not taken.
✓ Branch 11 → 6 taken 264 times.
✓ Branch 11 → 12 taken 298 times.
562 for (SourceFile *sourceFile : dependencies | std::views::values)
712
2/2
✓ Branch 7 → 8 taken 34 times.
✓ Branch 7 → 9 taken 230 times.
264 if (!sourceFile->isStdFile)
713
1/2
✓ Branch 8 → 9 taken 34 times.
✗ Branch 8 → 23 not taken.
34 sourceFile->collectAndPrintWarnings();
714 // Collect warnings for this file
715
2/2
✓ Branch 12 → 13 taken 296 times.
✓ Branch 12 → 15 taken 2 times.
298 if (!ignoreWarnings)
716 296 globalScope->collectWarnings(compilerOutput.warnings);
717 // Print warnings for this file
718
2/2
✓ Branch 21 → 17 taken 189 times.
✓ Branch 21 → 22 taken 298 times.
487 for (const CompilerWarning &warning : compilerOutput.warnings)
719
1/2
✓ Branch 18 → 19 taken 189 times.
✗ Branch 18 → 24 not taken.
189 warning.print();
720 298 }
721
722 4386 const SourceFile *SourceFile::getRootSourceFile() const { // NOLINT(misc-no-recursion)
723
2/2
✓ Branch 2 → 3 taken 1564 times.
✓ Branch 2 → 4 taken 2822 times.
4386 return isMainFile ? this : parent->getRootSourceFile();
724 }
725
726 11777 bool SourceFile::isRT(RuntimeModule runtimeModule) const {
727
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 11777 times.
11777 assert(IDENTIFYING_TOP_LEVEL_NAMES.contains(runtimeModule));
728 11777 const char *topLevelName = IDENTIFYING_TOP_LEVEL_NAMES.at(runtimeModule);
729
4/6
✓ Branch 8 → 9 taken 11777 times.
✗ Branch 8 → 26 not taken.
✓ Branch 9 → 10 taken 11777 times.
✗ Branch 9 → 24 not taken.
✓ Branch 12 → 13 taken 1785 times.
✓ Branch 12 → 14 taken 9992 times.
35331 if (!exportedNameRegistry.contains(topLevelName))
730 1785 return false;
731
2/4
✓ Branch 16 → 17 taken 9992 times.
✗ Branch 16 → 32 not taken.
✓ Branch 17 → 18 taken 9992 times.
✗ Branch 17 → 30 not taken.
29976 return exportedNameRegistry.at(topLevelName).targetEntry->scope == globalScope.get();
732 }
733
734 2971 bool SourceFile::haveAllDependantsBeenTypeChecked() const {
735 8873 return std::ranges::all_of(dependants, [](const SourceFile *dependant) { return dependant->totalTypeCheckerRuns >= 1; });
736 }
737
738 /**
739 * Acquire all publicly visible symbols from the imported source file and put them in the name registry of the current one.
740 * But only do that for the symbols that are actually defined in the imported source file. Do not allow transitive dependencies.
741 * Here, we also register privately visible symbols to know that the symbol exist. The error handling regarding the visibility
742 * is issued later in the pipeline.
743 *
744 * @param importedSourceFile Imported source file
745 * @param importName First fragment of all fully qualified symbol names from that import
746 */
747 1457 void SourceFile::mergeNameRegistries(const SourceFile &importedSourceFile, const std::string &importName) {
748 // Retrieve import entry
749 1457 SymbolTableEntry *importEntry = globalScope->lookupStrict(importName);
750
3/4
✓ Branch 6 → 7 taken 812 times.
✓ Branch 6 → 10 taken 645 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 812 times.
1457 assert(importEntry != nullptr || importName.starts_with("__")); // Runtime imports start with two underscores
751
752
2/2
✓ Branch 31 → 12 taken 40485 times.
✓ Branch 31 → 32 taken 1457 times.
41942 for (const auto &[originalName, entry] : importedSourceFile.exportedNameRegistry) {
753 // Skip if we introduce a transitive dependency
754
2/2
✓ Branch 16 → 17 taken 19810 times.
✓ Branch 16 → 18 taken 20675 times.
40485 if (entry.targetScope->sourceFile->globalScope != importedSourceFile.globalScope)
755 19810 continue;
756 // Add the fully qualified name
757
1/2
✓ Branch 18 → 19 taken 20675 times.
✗ Branch 18 → 44 not taken.
20675 std::string newName = importName;
758
1/2
✓ Branch 19 → 20 taken 20675 times.
✗ Branch 19 → 42 not taken.
20675 newName += SCOPE_ACCESS_TOKEN;
759
1/2
✓ Branch 20 → 21 taken 20675 times.
✗ Branch 20 → 42 not taken.
20675 newName += originalName;
760
1/2
✓ Branch 23 → 24 taken 20675 times.
✗ Branch 23 → 33 not taken.
20675 exportedNameRegistry.insert({newName, {newName, entry.typeId, entry.targetEntry, entry.targetScope, importEntry}});
761 // Add the shortened name, considering the name collision
762 20675 const bool keepOnCollision = importedSourceFile.alwaysKeepSymbolsOnNameCollision;
763
1/2
✓ Branch 26 → 27 taken 20675 times.
✗ Branch 26 → 42 not taken.
20675 addNameRegistryEntry(originalName, entry.typeId, entry.targetEntry, entry.targetScope, keepOnCollision, importEntry);
764 20675 }
765
2/6
✓ Branch 21 → 22 taken 20675 times.
✗ Branch 21 → 40 not taken.
✓ Branch 22 → 23 taken 20675 times.
✗ Branch 22 → 35 not taken.
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 39 not taken.
22132 }
766
767 205 void SourceFile::dumpCacheStats() {
768
1/2
✓ Branch 2 → 3 taken 205 times.
✗ Branch 2 → 32 not taken.
205 std::stringstream cacheStats;
769
3/6
✓ Branch 3 → 4 taken 205 times.
✗ Branch 3 → 22 not taken.
✓ Branch 4 → 5 taken 205 times.
✗ Branch 4 → 20 not taken.
✓ Branch 5 → 6 taken 205 times.
✗ Branch 5 → 20 not taken.
205 cacheStats << FunctionManager::dumpLookupCacheStatistics() << std::endl;
770
3/6
✓ Branch 7 → 8 taken 205 times.
✗ Branch 7 → 25 not taken.
✓ Branch 8 → 9 taken 205 times.
✗ Branch 8 → 23 not taken.
✓ Branch 9 → 10 taken 205 times.
✗ Branch 9 → 23 not taken.
205 cacheStats << StructManager::dumpLookupCacheStatistics() << std::endl;
771
3/6
✓ Branch 11 → 12 taken 205 times.
✗ Branch 11 → 28 not taken.
✓ Branch 12 → 13 taken 205 times.
✗ Branch 12 → 26 not taken.
✓ Branch 13 → 14 taken 205 times.
✗ Branch 13 → 26 not taken.
205 cacheStats << InterfaceManager::dumpLookupCacheStatistics() << std::endl;
772
1/2
✓ Branch 15 → 16 taken 205 times.
✗ Branch 15 → 29 not taken.
205 compilerOutput.cacheStats = cacheStats.str();
773 205 }
774
775 void SourceFile::dumpCompilationStats() const {
776 const size_t sourceFileCount = resourceManager.sourceFiles.size();
777 const size_t totalLineCount = resourceManager.getTotalLineCount();
778 const size_t totalTypeCount = TypeRegistry::getTypeCount();
779 const size_t allocatedBytes = resourceManager.astNodeAlloc.getTotalAllocatedSize();
780 const size_t allocationCount = resourceManager.astNodeAlloc.getAllocationCount();
781 const size_t totalDuration = resourceManager.totalTimer.getDurationMilliseconds();
782 std::cout << "\nSuccessfully compiled " << std::to_string(sourceFileCount) << " source file(s)";
783 std::cout << " or " << std::to_string(totalLineCount) << " lines in total.\n";
784 std::cout << "Total number of blocks allocated via BlockAllocator: " << CommonUtil::formatBytes(allocatedBytes);
785 std::cout << " in " << std::to_string(allocationCount) << " allocations.\n";
786 #ifndef NDEBUG
787 resourceManager.astNodeAlloc.printAllocatedClassStatistic();
788 #endif
789 std::cout << "Total number of types: " << std::to_string(totalTypeCount) << "\n";
790 std::cout << "Total compile time: " << std::to_string(totalDuration) << " ms\n";
791 }
792
793 void SourceFile::dumpOutput(const std::string &content, const std::string &caption, const std::string &fileSuffix) const {
794 if (cliOptions.dump.dumpToFiles) {
795 // Dump to file
796 const std::string dumpFileName = filePath.stem().string() + "-" + fileSuffix;
797 std::filesystem::path dumpFilePath = cliOptions.outputDir / dumpFileName;
798 dumpFilePath.make_preferred();
799 FileUtil::writeToFile(dumpFilePath, content);
800 } else {
801 // Dump to console
802 std::cout << "\n" << caption << ":\n" << content;
803 }
804
805 // If the abort after dump is requested, set the abort compilation flag
806 if (cliOptions.dump.abortAfterDump) {
807 // If this is an IR dump whilst having optimization enabled, we may not abort when dumping unoptimized IR,
808 // because we also have to dump the optimized IR
809 if (cliOptions.dump.dumpIR && fileSuffix == "ir-code.ll") {
810 resourceManager.abortCompilation = cliOptions.optLevel == OptLevel::O0;
811 } else {
812 resourceManager.abortCompilation = true;
813 }
814 }
815 }
816
817 1864 void SourceFile::visualizerPreamble(std::stringstream &output) const {
818
2/2
✓ Branch 2 → 3 taken 280 times.
✓ Branch 2 → 4 taken 1584 times.
1864 if (isMainFile)
819 280 output << "digraph {\n rankdir=\"TB\";\n";
820 else
821 1584 output << "subgraph {\n";
822
3/6
✓ Branch 6 → 7 taken 1864 times.
✗ Branch 6 → 13 not taken.
✓ Branch 7 → 8 taken 1864 times.
✗ Branch 7 → 11 not taken.
✓ Branch 8 → 9 taken 1864 times.
✗ Branch 8 → 11 not taken.
1864 output << " label=\"" << filePath.generic_string() << "\";\n";
823 1864 }
824
825 void SourceFile::visualizerOutput(std::string outputName, const std::string &output) const {
826 if (cliOptions.dump.dumpToFiles) {
827 // Check if graphviz is installed
828 // GCOV_EXCL_START
829 if (!SystemUtil::isGraphvizInstalled())
830 throw CompilerError(IO_ERROR, "Please check if you have installed Graphviz and added it to the PATH variable");
831 // GCOV_EXCL_STOP
832
833 // Write to a dot file
834 std::ranges::transform(outputName, outputName.begin(), ::tolower);
835 dumpOutput(output, outputName, outputName + ".dot");
836
837 // Generate SVG. This only works if the dot code was dumped into a file
838 std::cout << "\nGenerating SVG file ... ";
839 const std::string dotFileName = filePath.stem().string() + "-" + outputName + ".dot";
840 std::filesystem::path dotFilePath = cliOptions.outputDir / dotFileName;
841 std::filesystem::path svgFilePath = dotFilePath;
842 svgFilePath.replace_extension("svg");
843 dotFilePath.make_preferred();
844 svgFilePath.make_preferred();
845 SystemUtil::exec("dot -T svg -o" + svgFilePath.string() + " " + dotFilePath.string());
846 std::cout << "done.\nSVG file can be found at: " << svgFilePath << "\n";
847 } else {
848 // Dump to console
849 std::cout << "\nSerialized " << outputName << ":\n\n" << output << "\n";
850 }
851
852 // If the abort after dump is requested, set the abort compilation flag
853 if (cliOptions.dump.abortAfterDump)
854 resourceManager.abortCompilation = true;
855 }
856
857 14303 void SourceFile::printStatusMessage(const char *stage, const CompileStageIOType &in, const CompileStageIOType &out,
858 uint64_t stageRuntime, unsigned short stageRuns) const {
859
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 30 taken 14303 times.
14303 if (cliOptions.printDebugOutput) {
860 static constexpr const char *const compilerStageIoTypeName[6] = {"Code", "Tokens", "CST", "AST", "IR", "Obj"};
861 // Build output string
862 std::stringstream outputStr;
863 outputStr << "[" << stage << "] for " << fileName << ": ";
864 outputStr << compilerStageIoTypeName[in] << " --> " << compilerStageIoTypeName[out];
865 outputStr << " (" << std::to_string(stageRuntime) << " ms";
866 if (stageRuns > 0)
867 outputStr << "; " << std::to_string(stageRuns) << " run(s)";
868 outputStr << ")\n";
869 // Print
870 std::cout << outputStr.str();
871 }
872 14303 }
873
874 } // namespace spice::compiler
875