GCC Code Coverage Report


Directory: ../
File: src/SourceFile.cpp
Date: 2025-02-05 01:09:36
Exec Total Coverage
Lines: 438 514 85.2%
Functions: 39 41 95.1%
Branches: 451 986 45.7%

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