GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 86.2% 467 / 3 / 545
Functions: 93.0% 40 / 0 / 43
Branches: 48.0% 495 / 12 / 1044

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