GCC Code Coverage Report


Directory: ../
File: src/SourceFile.cpp
Date: 2024-12-24 01:17:15
Exec Total Coverage
Lines: 416 496 83.9%
Functions: 38 41 92.7%
Branches: 428 942 45.4%

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