GCC Code Coverage Report


Directory: ../
File: src/SourceFile.cpp
Date: 2025-05-08 21:00:15
Exec Total Coverage
Lines: 448 529 84.7%
Functions: 40 43 93.0%
Branches: 465 1014 45.9%

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