GCC Code Coverage Report


Directory: ../
File: src/SourceFile.cpp
Date: 2025-10-27 22:48:14
Coverage Exec Excl Total
Lines: 84.3% 441 3 526
Functions: 93.0% 40 0 43
Branches: 45.7% 459 12 1016

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