GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 90.1% 302 / 3 / 338
Functions: 84.4% 27 / 0 / 32
Branches: 50.9% 417 / 8 / 828

src/driver/Driver.cpp
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 #include "Driver.h"
4
5 #include <exception/CliError.h>
6 #include <util/CommonUtil.h>
7 #include <util/CompilerWarning.h>
8 #include <util/SystemUtil.h>
9
10 #include <llvm/Support/CommandLine.h>
11 #include <llvm/TargetParser/Host.h>
12 #include <llvm/TargetParser/Triple.h>
13
14 namespace spice::compiler {
15
16
3/6
✓ Branch 4 → 5 taken 1383 times.
✗ Branch 4 → 51 not taken.
✓ Branch 7 → 8 taken 1383 times.
✗ Branch 7 → 45 not taken.
✓ Branch 8 → 9 taken 1383 times.
✗ Branch 8 → 43 not taken.
5532 Driver::Driver(CliOptions &foreignCliOptions, bool dryRun) : cliOptions(foreignCliOptions), performDryRun(dryRun) {
17 // Allow positional args
18 1383 app.positionals_at_end();
19 1383 app.allow_extras(false);
20
2/4
✓ Branch 15 → 16 taken 1383 times.
✗ Branch 15 → 57 not taken.
✓ Branch 17 → 18 taken 1383 times.
✗ Branch 17 → 55 not taken.
1383 app.footer("(c) Marc Auberer 2021-" + std::to_string(CommonUtil::getCurrentYear()));
21
22 // Add version flag
23
3/6
✓ Branch 24 → 25 taken 1383 times.
✗ Branch 24 → 67 not taken.
✓ Branch 27 → 28 taken 1383 times.
✗ Branch 27 → 61 not taken.
✓ Branch 28 → 29 taken 1383 times.
✗ Branch 28 → 59 not taken.
5532 app.set_version_flag("--version,-v", CommonUtil::buildVersionInfo());
24
25 // Create sub-commands
26
1/2
✓ Branch 34 → 35 taken 1383 times.
✗ Branch 34 → 74 not taken.
1383 addBuildSubcommand();
27
1/2
✓ Branch 35 → 36 taken 1383 times.
✗ Branch 35 → 74 not taken.
1383 addRunSubcommand();
28
1/2
✓ Branch 36 → 37 taken 1383 times.
✗ Branch 36 → 74 not taken.
1383 addTestSubcommand();
29
1/2
✓ Branch 37 → 38 taken 1383 times.
✗ Branch 37 → 74 not taken.
1383 addInstallSubcommand();
30
1/2
✓ Branch 38 → 39 taken 1383 times.
✗ Branch 38 → 74 not taken.
1383 addUninstallSubcommand();
31
32 1383 app.final_callback([&] {
33 // Print help text for the root command if no sub-command was given
34
2/4
✓ Branch 2 → 3 taken 1371 times.
✗ Branch 2 → 124 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 15 taken 1371 times.
1371 if (app.get_subcommands().empty()) {
35 ✗ std::cout << app.help();
36 ✗ return;
37 }
38
39
4/4
✓ Branch 15 → 16 taken 1369 times.
✓ Branch 15 → 17 taken 2 times.
✓ Branch 16 → 17 taken 2 times.
✓ Branch 16 → 52 taken 1367 times.
1371 if (shouldInstall || shouldUninstall) {
40 // Prepare the installation path
41
1/2
✓ Branch 17 → 18 taken 4 times.
✗ Branch 17 → 152 not taken.
4 std::filesystem::path installPath = SystemUtil::getSpiceBinDir();
42
2/4
✓ Branch 18 → 19 taken 4 times.
✗ Branch 18 → 136 not taken.
✓ Branch 19 → 20 taken 4 times.
✗ Branch 19 → 134 not taken.
4 installPath /= cliOptions.mainSourceFile.stem();
43
1/2
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 4 times.
4 if (!performDryRun)
44 ✗ create_directories(installPath);
45
1/2
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 4 times.
4 assert(cliOptions.outputContainer == OutputContainer::EXECUTABLE);
46
3/6
✓ Branch 25 → 26 taken 4 times.
✗ Branch 25 → 139 not taken.
✓ Branch 26 → 27 taken 4 times.
✗ Branch 26 → 139 not taken.
✓ Branch 27 → 28 taken 4 times.
✗ Branch 27 → 137 not taken.
4 installPath.replace_extension(SystemUtil::getOutputFileExtension(cliOptions, OutputContainer::EXECUTABLE));
47
48 // If the binary should be installed, set the output path to the Spice bin directory
49
2/2
✓ Branch 29 → 30 taken 2 times.
✓ Branch 29 → 31 taken 2 times.
4 if (shouldInstall)
50
1/2
✓ Branch 30 → 31 taken 2 times.
✗ Branch 30 → 150 not taken.
2 cliOptions.outputPath = installPath;
51
52 // If the binary should be uninstalled, check if the executable exists and uninstall it
53
3/4
✓ Branch 31 → 32 taken 2 times.
✓ Branch 31 → 50 taken 2 times.
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 50 taken 2 times.
4 if (shouldUninstall && !performDryRun) {
54 ✗ if (exists(installPath) && std::filesystem::remove(installPath))
55 ✗ std::cout << "Successfully uninstalled.\n";
56 else
57 ✗ CompilerWarning(UNINSTALL_FAILED, "The executable was not found at the expected location").print();
58 }
59 4 }
60
61 // Abort here if we do not need to compile
62
2/2
✓ Branch 52 → 53 taken 2 times.
✓ Branch 52 → 54 taken 1369 times.
1371 if (!shouldCompile)
63 2 return;
64
65 // Set output path and dir
66
2/2
✓ Branch 54 → 55 taken 8 times.
✓ Branch 54 → 82 taken 1361 times.
1369 if (shouldExecute) {
67 8 cliOptions.execute = true;
68 const uint64_t millis =
69
1/2
✓ Branch 57 → 58 taken 8 times.
✗ Branch 57 → 153 not taken.
8 duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
70
8/16
✓ Branch 59 → 60 taken 8 times.
✗ Branch 59 → 175 not taken.
✓ Branch 60 → 61 taken 8 times.
✗ Branch 60 → 173 not taken.
✓ Branch 61 → 62 taken 8 times.
✗ Branch 61 → 169 not taken.
✓ Branch 62 → 63 taken 8 times.
✗ Branch 62 → 165 not taken.
✓ Branch 63 → 64 taken 8 times.
✗ Branch 63 → 162 not taken.
✓ Branch 64 → 65 taken 8 times.
✗ Branch 64 → 160 not taken.
✓ Branch 65 → 66 taken 8 times.
✗ Branch 65 → 158 not taken.
✓ Branch 66 → 67 taken 8 times.
✗ Branch 66 → 156 not taken.
8 cliOptions.outputDir = std::filesystem::temp_directory_path() / "spice" / "output" / std::to_string(millis);
71
2/4
✓ Branch 76 → 77 taken 8 times.
✗ Branch 76 → 180 not taken.
✓ Branch 77 → 78 taken 8 times.
✗ Branch 77 → 178 not taken.
8 cliOptions.outputPath = cliOptions.outputDir / cliOptions.mainSourceFile.filename();
72
2/2
✓ Branch 83 → 84 taken 2 times.
✓ Branch 83 → 99 taken 1359 times.
1361 } else if (!cliOptions.outputPath.empty()) {
73
1/2
✗ Branch 85 → 86 not taken.
✓ Branch 85 → 93 taken 2 times.
2 if (is_directory(cliOptions.outputPath)) {
74 ✗ cliOptions.outputDir = cliOptions.outputPath;
75 ✗ cliOptions.outputPath = cliOptions.outputDir / cliOptions.mainSourceFile.filename();
76
1/2
✓ Branch 94 → 95 taken 2 times.
✗ Branch 94 → 106 not taken.
2 } else if (cliOptions.outputPath.has_parent_path()) {
77
1/2
✓ Branch 95 → 96 taken 2 times.
✗ Branch 95 → 186 not taken.
2 cliOptions.outputDir = cliOptions.outputPath.parent_path();
78 }
79 } else {
80 1359 cliOptions.outputDir = "./";
81
2/4
✓ Branch 100 → 101 taken 1359 times.
✗ Branch 100 → 189 not taken.
✓ Branch 101 → 102 taken 1359 times.
✗ Branch 101 → 187 not taken.
1359 cliOptions.outputPath = cliOptions.outputDir / cliOptions.mainSourceFile.filename();
82 }
83
84 // Set output file extension
85
3/6
✓ Branch 106 → 107 taken 1369 times.
✗ Branch 106 → 193 not taken.
✓ Branch 107 → 108 taken 1369 times.
✗ Branch 107 → 193 not taken.
✓ Branch 108 → 109 taken 1369 times.
✗ Branch 108 → 191 not taken.
1369 cliOptions.outputPath.replace_extension(SystemUtil::getOutputFileExtension(cliOptions, cliOptions.outputContainer));
86
87 // Set cache dir
88
5/10
✓ Branch 110 → 111 taken 1369 times.
✗ Branch 110 → 206 not taken.
✓ Branch 111 → 112 taken 1369 times.
✗ Branch 111 → 202 not taken.
✓ Branch 112 → 113 taken 1369 times.
✗ Branch 112 → 199 not taken.
✓ Branch 113 → 114 taken 1369 times.
✗ Branch 113 → 197 not taken.
✓ Branch 114 → 115 taken 1369 times.
✗ Branch 114 → 195 not taken.
1369 cliOptions.cacheDir = std::filesystem::temp_directory_path() / "spice" / "cache";
89
90 // Create directories in case they not exist yet
91 1369 create_directories(cliOptions.cacheDir);
92 1369 create_directories(cliOptions.outputDir);
93 });
94 1383 }
95
96 /**
97 * Start the parsing process
98 *
99 * @param argc Argument count
100 * @param argv Argument vector
101 * @return Return code
102 */
103 1383 int Driver::parse(int argc, const char *argv[]) {
104 try {
105
2/2
✓ Branch 2 → 3 taken 1371 times.
✓ Branch 2 → 5 taken 2 times.
1383 app.parse(argc, argv);
106 1371 return EXIT_SUCCESS;
107
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 2 times.
2 } catch (const CLI::ParseError &parseError) {
108
1/2
✓ Branch 8 → 9 taken 2 times.
✗ Branch 8 → 11 not taken.
2 return app.exit(parseError);
109 2 }
110 }
111
112 /**
113 * Initialize the cli options based on the input of the user
114 */
115 1353 void Driver::enrich() const {
116 // Make path of given main source file canonical and relative
117
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 9 taken 1353 times.
1353 if (!performDryRun)
118 ✗ cliOptions.mainSourceFile = relative(cliOptions.mainSourceFile);
119
120 // Propagate llvm args to llvm
121
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 39 taken 1353 times.
1353 if (!cliOptions.llvmArgs.empty()) {
122 ✗ const std::vector<std::string> result = CommonUtil::split("llvm " + cliOptions.llvmArgs);
123 ✗ std::vector<const char *> resultCStr;
124 ✗ resultCStr.reserve(result.size());
125 ✗ for (const std::string &str : result)
126 ✗ resultCStr.push_back(str.c_str());
127 ✗ llvm::cl::ParseCommandLineOptions(static_cast<int>(result.size()), resultCStr.data());
128 ✗ }
129
130 // Propagate target information
131
3/6
✓ Branch 39 → 40 taken 1353 times.
✗ Branch 39 → 254 not taken.
✓ Branch 41 → 42 taken 1353 times.
✗ Branch 41 → 252 not taken.
✓ Branch 42 → 43 taken 1353 times.
✗ Branch 42 → 250 not taken.
1353 const llvm::Triple defaultTriple(llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()));
132
2/2
✓ Branch 46 → 47 taken 1339 times.
✓ Branch 46 → 65 taken 14 times.
1353 if (cliOptions.targetTriple.empty()) {
133
2/4
✓ Branch 47 → 48 taken 1339 times.
✗ Branch 47 → 387 not taken.
✓ Branch 48 → 49 taken 1339 times.
✗ Branch 48 → 57 not taken.
1339 if (cliOptions.targetArch == TARGET_UNKNOWN) { // We have nothing -> obtain native triplet
134
1/2
✓ Branch 49 → 50 taken 1339 times.
✗ Branch 49 → 387 not taken.
1339 cliOptions.targetTriple = defaultTriple;
135
2/4
✓ Branch 50 → 51 taken 1339 times.
✗ Branch 50 → 257 not taken.
✓ Branch 51 → 52 taken 1339 times.
✗ Branch 51 → 257 not taken.
1339 cliOptions.targetArch = defaultTriple.getArchName();
136
2/4
✓ Branch 52 → 53 taken 1339 times.
✗ Branch 52 → 258 not taken.
✓ Branch 53 → 54 taken 1339 times.
✗ Branch 53 → 258 not taken.
1339 cliOptions.targetVendor = defaultTriple.getVendorName();
137
2/4
✓ Branch 54 → 55 taken 1339 times.
✗ Branch 54 → 259 not taken.
✓ Branch 55 → 56 taken 1339 times.
✗ Branch 55 → 259 not taken.
1339 cliOptions.targetOs = defaultTriple.getOSName();
138 1339 cliOptions.isNativeTarget = true;
139 } else { // We have arch, vendor and os -> obtain triplet
140 ✗ cliOptions.targetTriple = llvm::Triple(cliOptions.targetArch, cliOptions.targetVendor, cliOptions.targetOs);
141 ✗ cliOptions.isNativeTarget = cliOptions.targetTriple == defaultTriple;
142 }
143 } else { // Obtain arch, vendor and os by the triplet
144
2/4
✓ Branch 65 → 66 taken 14 times.
✗ Branch 65 → 266 not taken.
✓ Branch 66 → 67 taken 14 times.
✗ Branch 66 → 264 not taken.
14 const llvm::Triple triple(cliOptions.targetTriple.normalize());
145
2/4
✓ Branch 68 → 69 taken 14 times.
✗ Branch 68 → 267 not taken.
✓ Branch 69 → 70 taken 14 times.
✗ Branch 69 → 267 not taken.
14 cliOptions.targetArch = triple.getArchName();
146
2/4
✓ Branch 70 → 71 taken 14 times.
✗ Branch 70 → 268 not taken.
✓ Branch 71 → 72 taken 14 times.
✗ Branch 71 → 268 not taken.
14 cliOptions.targetVendor = triple.getVendorName();
147
2/4
✓ Branch 72 → 73 taken 14 times.
✗ Branch 72 → 269 not taken.
✓ Branch 73 → 74 taken 14 times.
✗ Branch 73 → 269 not taken.
14 cliOptions.targetOs = triple.getOSName();
148
1/2
✓ Branch 74 → 75 taken 14 times.
✗ Branch 74 → 270 not taken.
14 cliOptions.isNativeTarget = triple == defaultTriple;
149 14 }
150
151 // Always preserve IR value names when dumping IR
152
2/2
✓ Branch 77 → 78 taken 2 times.
✓ Branch 77 → 79 taken 1351 times.
1353 if (cliOptions.dump.dumpIR)
153 2 cliOptions.namesForIRValues = true;
154
155 // Enable test mode when test mode was selected
156
2/2
✓ Branch 79 → 80 taken 4 times.
✓ Branch 79 → 81 taken 1349 times.
1353 if (cliOptions.buildMode == BuildMode::TEST) {
157 4 cliOptions.noEntryFct = true;
158 4 cliOptions.generateTestMain = true;
159 }
160
161 1353 const Sanitizer sanitizer = cliOptions.instrumentation.sanitizer;
162 // Memory sanitizer is only supported on Linux
163
4/6
✓ Branch 82 → 83 taken 8 times.
✓ Branch 82 → 85 taken 1345 times.
✗ Branch 83 → 84 not taken.
✓ Branch 83 → 85 taken 8 times.
✗ Branch 86 → 87 not taken.
✓ Branch 86 → 95 taken 1353 times.
1353 if (!cliOptions.targetTriple.isOSLinux() && sanitizer == Sanitizer::MEMORY)
164 ✗ throw CliError(FEATURE_NOT_SUPPORTED_FOR_TARGET, "Memory sanitizer is only supported for Linux targets");
165 // Some sanitizers need lifetime markers to work properly
166
4/4
✓ Branch 95 → 96 taken 1342 times.
✓ Branch 95 → 97 taken 11 times.
✓ Branch 96 → 97 taken 4 times.
✓ Branch 96 → 98 taken 1338 times.
1353 if (sanitizer == Sanitizer::ADDRESS || sanitizer == Sanitizer::MEMORY)
167 15 cliOptions.useLifetimeMarkers = true;
168 // Type sanitizer needs TBAA metadata to work properly
169
2/2
✓ Branch 98 → 99 taken 2 times.
✓ Branch 98 → 100 taken 1351 times.
1353 if (sanitizer == Sanitizer::TYPE)
170 2 cliOptions.useTBAAMetadata = true;
171
172 // Code coverage instrumentation needs debug line info to map counters back to source locations
173
2/2
✓ Branch 100 → 101 taken 659 times.
✓ Branch 100 → 123 taken 694 times.
1353 if (cliOptions.instrumentation.codeCoverage) {
174
2/2
✓ Branch 103 → 104 taken 652 times.
✓ Branch 103 → 114 taken 7 times.
1318 if (!cliOptions.instrumentation.emitsDebugInfo()) {
175 // Turning debug info off on purpose would leave the counters unmappable, so say so instead of overriding it
176
2/2
✓ Branch 104 → 105 taken 2 times.
✓ Branch 104 → 113 taken 650 times.
652 if (debugInfoLevelSetExplicitly)
177
2/4
✓ Branch 108 → 109 taken 2 times.
✗ Branch 108 → 285 not taken.
✓ Branch 109 → 110 taken 2 times.
✗ Branch 109 → 282 not taken.
6 throw CliError(INCOMPATIBLE_OPTIONS, "Code coverage instrumentation requires debug line info");
178 650 cliOptions.instrumentation.debugInfoLevel = DebugInfoLevel::LINE_ONLY;
179 }
180
2/2
✓ Branch 114 → 115 taken 2 times.
✓ Branch 114 → 123 taken 655 times.
657 if (cliOptions.useLTO)
181
2/4
✓ Branch 118 → 119 taken 2 times.
✗ Branch 118 → 294 not taken.
✓ Branch 119 → 120 taken 2 times.
✗ Branch 119 → 291 not taken.
6 throw CliError(INCOMPATIBLE_OPTIONS, "Code coverage instrumentation is not supported in combination with LTO");
182 }
183
184 // Infer build vars from other options
185
2/2
✓ Branch 2 → 3 taken 2688 times.
✓ Branch 2 → 4 taken 4057 times.
6745 const auto boolToString = [](bool input) { return input ? "true" : "false"; };
186
3/6
✓ Branch 126 → 127 taken 1349 times.
✗ Branch 126 → 302 not taken.
✓ Branch 127 → 128 taken 1349 times.
✗ Branch 127 → 300 not taken.
✓ Branch 128 → 129 taken 1349 times.
✗ Branch 128 → 300 not taken.
2698 cliOptions.buildVars["spice.is_debug"] = boolToString(cliOptions.buildMode == BuildMode::DEBUG);
187
3/6
✓ Branch 134 → 135 taken 1349 times.
✗ Branch 134 → 308 not taken.
✓ Branch 135 → 136 taken 1349 times.
✗ Branch 135 → 306 not taken.
✓ Branch 136 → 137 taken 1349 times.
✗ Branch 136 → 306 not taken.
2698 cliOptions.buildVars["spice.is_release"] = boolToString(cliOptions.buildMode == BuildMode::RELEASE);
188
3/6
✓ Branch 142 → 143 taken 1349 times.
✗ Branch 142 → 314 not taken.
✓ Branch 143 → 144 taken 1349 times.
✗ Branch 143 → 312 not taken.
✓ Branch 144 → 145 taken 1349 times.
✗ Branch 144 → 312 not taken.
2698 cliOptions.buildVars["spice.is_test"] = boolToString(cliOptions.buildMode == BuildMode::TEST);
189
3/6
✓ Branch 150 → 151 taken 1349 times.
✗ Branch 150 → 320 not taken.
✓ Branch 151 → 152 taken 1349 times.
✗ Branch 151 → 318 not taken.
✓ Branch 152 → 153 taken 1349 times.
✗ Branch 152 → 318 not taken.
2698 cliOptions.buildVars["spice.link_static"] = boolToString(cliOptions.staticLinking);
190
3/6
✓ Branch 158 → 159 taken 1349 times.
✗ Branch 158 → 326 not taken.
✓ Branch 159 → 160 taken 1349 times.
✗ Branch 159 → 324 not taken.
✓ Branch 160 → 161 taken 1349 times.
✗ Branch 160 → 324 not taken.
2698 cliOptions.buildVars["spice.is_native_target"] = boolToString(cliOptions.isNativeTarget);
191
3/6
✓ Branch 166 → 167 taken 1349 times.
✗ Branch 166 → 332 not taken.
✓ Branch 167 → 168 taken 1349 times.
✗ Branch 167 → 330 not taken.
✓ Branch 168 → 169 taken 1349 times.
✗ Branch 168 → 330 not taken.
2698 cliOptions.buildVars["spice.target_triple"] = cliOptions.targetTriple.str();
192
5/8
✓ Branch 171 → 172 taken 9 times.
✓ Branch 171 → 173 taken 1340 times.
✓ Branch 176 → 177 taken 1349 times.
✗ Branch 176 → 338 not taken.
✓ Branch 177 → 178 taken 1349 times.
✗ Branch 177 → 336 not taken.
✓ Branch 178 → 179 taken 1349 times.
✗ Branch 178 → 336 not taken.
2698 cliOptions.buildVars["spice.backend"] = cliOptions.backend == Backend::TPDE ? BACKEND_TPDE : BACKEND_LLVM;
193
194 // Prevent incompatible option combinations
195
3/4
✓ Branch 181 → 182 taken 2 times.
✓ Branch 181 → 191 taken 1347 times.
✓ Branch 182 → 183 taken 2 times.
✗ Branch 182 → 191 not taken.
1349 if (cliOptions.staticLinking && cliOptions.outputContainer == OutputContainer::SHARED_LIBRARY)
196
2/4
✓ Branch 186 → 187 taken 2 times.
✗ Branch 186 → 345 not taken.
✓ Branch 187 → 188 taken 2 times.
✗ Branch 187 → 342 not taken.
6 throw CliError(INCOMPATIBLE_OPTIONS, "Cannot link statically if compiling shared library");
197
198 // Guards for the experimental TPDE backend — ELF only, x86_64/aarch64 only, no LTO
199
2/2
✓ Branch 191 → 192 taken 9 times.
✓ Branch 191 → 233 taken 1338 times.
1347 if (cliOptions.backend == Backend::TPDE) {
200
2/2
✓ Branch 192 → 193 taken 2 times.
✓ Branch 192 → 201 taken 7 times.
9 if (cliOptions.useLTO)
201
2/4
✓ Branch 196 → 197 taken 2 times.
✗ Branch 196 → 354 not taken.
✓ Branch 197 → 198 taken 2 times.
✗ Branch 197 → 351 not taken.
6 throw CliError(INCOMPATIBLE_OPTIONS, "The TPDE backend does not support LTO");
202
2/2
✓ Branch 201 → 202 taken 2 times.
✓ Branch 201 → 210 taken 5 times.
7 if (cliOptions.instrumentation.codeCoverage)
203
2/4
✓ Branch 205 → 206 taken 2 times.
✗ Branch 205 → 363 not taken.
✓ Branch 206 → 207 taken 2 times.
✗ Branch 206 → 360 not taken.
6 throw CliError(INCOMPATIBLE_OPTIONS, "The TPDE backend does not support code coverage instrumentation");
204
2/2
✓ Branch 211 → 212 taken 2 times.
✓ Branch 211 → 220 taken 3 times.
5 if (!cliOptions.targetTriple.isOSLinux())
205
2/4
✓ Branch 215 → 216 taken 2 times.
✗ Branch 215 → 372 not taken.
✓ Branch 216 → 217 taken 2 times.
✗ Branch 216 → 369 not taken.
6 throw CliError(FEATURE_NOT_SUPPORTED_FOR_TARGET, "The TPDE backend only supports ELF targets (Linux)");
206 3 const llvm::Triple::ArchType arch = cliOptions.targetTriple.getArch();
207
3/4
✓ Branch 221 → 222 taken 2 times.
✓ Branch 221 → 231 taken 1 time.
✓ Branch 222 → 223 taken 2 times.
✗ Branch 222 → 231 not taken.
3 if (arch != llvm::Triple::x86_64 && arch != llvm::Triple::aarch64)
208
2/4
✓ Branch 226 → 227 taken 2 times.
✗ Branch 226 → 381 not taken.
✓ Branch 227 → 228 taken 2 times.
✗ Branch 227 → 378 not taken.
6 throw CliError(FEATURE_NOT_SUPPORTED_FOR_TARGET, "The TPDE backend only supports x86_64 and aarch64");
209
1/2
✗ Branch 231 → 232 not taken.
✓ Branch 231 → 233 taken 1 time.
1 if (cliOptions.optLevel != OptLevel::O0)
210 ✗ std::cout << "\033[33mWarning: the TPDE backend does not optimize; -O flag will be ignored\033[0m\n";
211 }
212 1353 }
213
214 /**
215 * Executes the built executable
216 */
217 ✗ void Driver::runBinary() const {
218 // Print status message
219 ✗ if (cliOptions.printDebugOutput)
220 ✗ std::cout << "Running executable ...\n\n";
221
222 // Run executable, inheriting our standard streams so its output goes straight to the terminal
223 ✗ std::filesystem::path executablePath = cliOptions.outputPath;
224 ✗ executablePath.make_preferred();
225 ✗ const int exitCode = SystemUtil::run(executablePath.string());
226 ✗ if (exitCode != EXIT_SUCCESS)
227 ✗ throw CliError(NON_ZERO_EXIT_CODE, "Your Spice executable exited with non-zero exit code " + std::to_string(exitCode));
228 ✗ }
229
230 /**
231 * Add build subcommand to cli interface
232 */
233 1383 void Driver::addBuildSubcommand() {
234 // Create sub-command itself
235
3/6
✓ Branch 4 → 5 taken 1383 times.
✗ Branch 4 → 171 not taken.
✓ Branch 7 → 8 taken 1383 times.
✗ Branch 7 → 165 not taken.
✓ Branch 8 → 9 taken 1383 times.
✗ Branch 8 → 163 not taken.
5532 CLI::App *subCmd = app.add_subcommand("build", "Builds your Spice program and emits an executable");
236
2/4
✓ Branch 15 → 16 taken 1383 times.
✗ Branch 15 → 177 not taken.
✓ Branch 16 → 17 taken 1383 times.
✗ Branch 16 → 175 not taken.
2766 subCmd->alias("b");
237 1383 subCmd->allow_non_standard_option_names();
238 1383 subCmd->configurable();
239 1383 subCmd->callback([&] { shouldCompile = true; });
240
241
1/2
✓ Branch 24 → 25 taken 1383 times.
✗ Branch 24 → 331 not taken.
1383 addCompileSubcommandOptions(subCmd);
242
1/2
✓ Branch 25 → 26 taken 1383 times.
✗ Branch 25 → 331 not taken.
1383 addInstrumentationOptions(subCmd);
243
244 1397 const auto outputContainerCallback = [&](const CLI::results_t &results) {
245
1/2
✓ Branch 3 → 4 taken 14 times.
✗ Branch 3 → 29 not taken.
14 std::string inputString = results.front();
246
1/2
✓ Branch 5 → 6 taken 14 times.
✗ Branch 5 → 27 not taken.
14 std::ranges::transform(inputString, inputString.begin(), tolower);
247
248
3/4
✓ Branch 6 → 7 taken 14 times.
✗ Branch 6 → 27 not taken.
✓ Branch 7 → 8 taken 4 times.
✓ Branch 7 → 9 taken 10 times.
14 if (inputString == OUTPUT_CONTAINER_EXECUTABLE)
249 4 cliOptions.outputContainer = OutputContainer::EXECUTABLE;
250
3/4
✓ Branch 9 → 10 taken 10 times.
✗ Branch 9 → 27 not taken.
✓ Branch 10 → 11 taken 2 times.
✓ Branch 10 → 12 taken 8 times.
10 else if (inputString == OUTPUT_CONTAINER_OBJECT_FILE)
251 2 cliOptions.outputContainer = OutputContainer::OBJECT_FILE;
252
3/4
✓ Branch 12 → 13 taken 8 times.
✗ Branch 12 → 27 not taken.
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 15 taken 6 times.
8 else if (inputString == OUTPUT_CONTAINER_STATIC_LIBRARY)
253 2 cliOptions.outputContainer = OutputContainer::STATIC_LIBRARY;
254
3/4
✓ Branch 15 → 16 taken 6 times.
✗ Branch 15 → 27 not taken.
✓ Branch 16 → 17 taken 4 times.
✓ Branch 16 → 18 taken 2 times.
6 else if (inputString == OUTPUT_CONTAINER_SHARED_LIBRARY)
255 4 cliOptions.outputContainer = OutputContainer::SHARED_LIBRARY;
256 else
257
1/2
✓ Branch 19 → 20 taken 2 times.
✗ Branch 19 → 24 not taken.
2 throw CliError(INVALID_OUTPUT_CONTAINER, inputString);
258
259 12 return true;
260 14 };
261
262 // --output-container
263
2/4
✓ Branch 33 → 34 taken 1383 times.
✗ Branch 33 → 183 not taken.
✓ Branch 34 → 35 taken 1383 times.
✗ Branch 34 → 181 not taken.
5532 subCmd->add_option("--output-container", outputContainerCallback,
264
1/2
✓ Branch 29 → 30 taken 1383 times.
✗ Branch 29 → 192 not taken.
4149 "Format of the compilation output container: exec (default), obj, lib, dylib)");
265 // --target-triple
266
2/4
✓ Branch 46 → 47 taken 1383 times.
✗ Branch 46 → 201 not taken.
✓ Branch 47 → 48 taken 1383 times.
✗ Branch 47 → 199 not taken.
4149 subCmd->add_option<llvm::Triple>("--target,--target-triple,-t", cliOptions.targetTriple,
267
1/2
✓ Branch 43 → 44 taken 1383 times.
✗ Branch 43 → 207 not taken.
2766 "Target triple for the emitted executable (for cross-compiling)");
268 // --target-arch
269
2/4
✓ Branch 57 → 58 taken 1383 times.
✗ Branch 57 → 213 not taken.
✓ Branch 58 → 59 taken 1383 times.
✗ Branch 58 → 211 not taken.
4149 subCmd->add_option<std::string>("--target-arch", cliOptions.targetArch,
270
1/2
✓ Branch 54 → 55 taken 1383 times.
✗ Branch 54 → 219 not taken.
2766 "Target arch for emitted executable (for cross-compiling)");
271 // --target-vendor
272
2/4
✓ Branch 68 → 69 taken 1383 times.
✗ Branch 68 → 225 not taken.
✓ Branch 69 → 70 taken 1383 times.
✗ Branch 69 → 223 not taken.
4149 subCmd->add_option<std::string>("--target-vendor", cliOptions.targetVendor,
273
1/2
✓ Branch 65 → 66 taken 1383 times.
✗ Branch 65 → 231 not taken.
2766 "Target vendor for emitted executable (for cross-compiling)");
274 // --target-os
275
3/6
✓ Branch 76 → 77 taken 1383 times.
✗ Branch 76 → 243 not taken.
✓ Branch 79 → 80 taken 1383 times.
✗ Branch 79 → 237 not taken.
✓ Branch 80 → 81 taken 1383 times.
✗ Branch 80 → 235 not taken.
5532 subCmd->add_option<std::string>("--target-os", cliOptions.targetOs, "Target os for emitted executable (for cross-compiling)");
276 // --output
277
3/6
✓ Branch 87 → 88 taken 1383 times.
✗ Branch 87 → 255 not taken.
✓ Branch 90 → 91 taken 1383 times.
✗ Branch 90 → 249 not taken.
✓ Branch 91 → 92 taken 1383 times.
✗ Branch 91 → 247 not taken.
5532 subCmd->add_option<std::filesystem::path>("--output,-o", cliOptions.outputPath, "Set the output file path");
278 // --disable-verifier
279
3/6
✓ Branch 98 → 99 taken 1383 times.
✗ Branch 98 → 267 not taken.
✓ Branch 101 → 102 taken 1383 times.
✗ Branch 101 → 261 not taken.
✓ Branch 102 → 103 taken 1383 times.
✗ Branch 102 → 259 not taken.
5532 subCmd->add_flag<bool>("--disable-verifier", cliOptions.disableVerifier, "Disable LLVM module and function verification");
280 // --no-entry
281
3/6
✓ Branch 109 → 110 taken 1383 times.
✗ Branch 109 → 279 not taken.
✓ Branch 112 → 113 taken 1383 times.
✗ Branch 112 → 273 not taken.
✓ Branch 113 → 114 taken 1383 times.
✗ Branch 113 → 271 not taken.
5532 subCmd->add_flag<bool>("--no-entry", cliOptions.noEntryFct, "Do not generate main function");
282 // --static
283
3/6
✓ Branch 120 → 121 taken 1383 times.
✗ Branch 120 → 291 not taken.
✓ Branch 123 → 124 taken 1383 times.
✗ Branch 123 → 285 not taken.
✓ Branch 124 → 125 taken 1383 times.
✗ Branch 124 → 283 not taken.
5532 subCmd->add_flag<bool>("--static", cliOptions.staticLinking, "Link statically");
284 // --strip-symbols
285
2/4
✓ Branch 134 → 135 taken 1383 times.
✗ Branch 134 → 297 not taken.
✓ Branch 135 → 136 taken 1383 times.
✗ Branch 135 → 295 not taken.
4149 subCmd->add_flag<bool>("--strip-symbols", cliOptions.stripSymbols,
286
1/2
✓ Branch 131 → 132 taken 1383 times.
✗ Branch 131 → 303 not taken.
2766 "Strip the symbol table from the executable, making it smaller at the price of unreadable "
287 "stack traces and, together with --debug-info, no debug info either");
288 // --dump-to-files
289
3/6
✓ Branch 142 → 143 taken 1383 times.
✗ Branch 142 → 315 not taken.
✓ Branch 145 → 146 taken 1383 times.
✗ Branch 145 → 309 not taken.
✓ Branch 146 → 147 taken 1383 times.
✗ Branch 146 → 307 not taken.
5532 subCmd->add_flag<bool>("--dump-to-files", cliOptions.dump.dumpToFiles, "Redirect dumps to files instead of printing");
290 // --abort-after-dump
291
2/4
✓ Branch 156 → 157 taken 1383 times.
✗ Branch 156 → 321 not taken.
✓ Branch 157 → 158 taken 1383 times.
✗ Branch 157 → 319 not taken.
4149 subCmd->add_flag<bool>("--abort-after-dump", cliOptions.dump.abortAfterDump,
292
1/2
✓ Branch 153 → 154 taken 1383 times.
✗ Branch 153 → 327 not taken.
2766 "Abort the compilation process after dumping the first requested resource");
293 1383 }
294
295 /**
296 * Add run subcommand to cli interface
297 */
298 1383 void Driver::addRunSubcommand() {
299 // Create sub-command itself
300
3/6
✓ Branch 4 → 5 taken 1383 times.
✗ Branch 4 → 45 not taken.
✓ Branch 7 → 8 taken 1383 times.
✗ Branch 7 → 39 not taken.
✓ Branch 8 → 9 taken 1383 times.
✗ Branch 8 → 37 not taken.
5532 CLI::App *subCmd = app.add_subcommand("run", "Builds your Spice program and runs it immediately");
301
2/4
✓ Branch 15 → 16 taken 1383 times.
✗ Branch 15 → 51 not taken.
✓ Branch 16 → 17 taken 1383 times.
✗ Branch 16 → 49 not taken.
2766 subCmd->alias("r");
302 1383 subCmd->allow_non_standard_option_names();
303 1383 subCmd->callback([&] { shouldCompile = shouldExecute = true; });
304
305 1383 addCompileSubcommandOptions(subCmd);
306 1383 addInstrumentationOptions(subCmd);
307
308 // --disable-verifier
309
3/6
✓ Branch 27 → 28 taken 1383 times.
✗ Branch 27 → 63 not taken.
✓ Branch 30 → 31 taken 1383 times.
✗ Branch 30 → 57 not taken.
✓ Branch 31 → 32 taken 1383 times.
✗ Branch 31 → 55 not taken.
5532 subCmd->add_flag<bool>("--disable-verifier", cliOptions.disableVerifier, "Disable LLVM module and function verification");
310 1383 }
311
312 /**
313 * Add test subcommand to cli interface
314 */
315 1383 void Driver::addTestSubcommand() {
316 // Create sub-command itself
317
3/6
✓ Branch 4 → 5 taken 1383 times.
✗ Branch 4 → 45 not taken.
✓ Branch 7 → 8 taken 1383 times.
✗ Branch 7 → 39 not taken.
✓ Branch 8 → 9 taken 1383 times.
✗ Branch 8 → 37 not taken.
5532 CLI::App *subCmd = app.add_subcommand("test", "Builds your Spice program and runs all enclosed tests");
318
2/4
✓ Branch 15 → 16 taken 1383 times.
✗ Branch 15 → 51 not taken.
✓ Branch 16 → 17 taken 1383 times.
✗ Branch 16 → 49 not taken.
2766 subCmd->alias("t");
319 1383 subCmd->allow_non_standard_option_names();
320 1383 subCmd->callback([&] {
321 4 shouldCompile = shouldExecute = true;
322 4 cliOptions.buildMode = BuildMode::TEST;
323 4 cliOptions.generateTestMain = true; // An alternative entry function is generated
324 4 cliOptions.noEntryFct = true; // To not have two main functions, disable normal main
325 4 });
326
327 1383 addCompileSubcommandOptions(subCmd);
328 1383 addInstrumentationOptions(subCmd);
329
330 // --disable-verifier
331
3/6
✓ Branch 27 → 28 taken 1383 times.
✗ Branch 27 → 63 not taken.
✓ Branch 30 → 31 taken 1383 times.
✗ Branch 30 → 57 not taken.
✓ Branch 31 → 32 taken 1383 times.
✗ Branch 31 → 55 not taken.
5532 subCmd->add_flag<bool>("--disable-verifier", cliOptions.disableVerifier, "Disable LLVM module and function verification");
332 1383 }
333
334 /**
335 * Add install subcommand to cli interface
336 */
337 1383 void Driver::addInstallSubcommand() {
338 // Create sub-command itself
339
3/6
✓ Branch 4 → 5 taken 1383 times.
✗ Branch 4 → 33 not taken.
✓ Branch 7 → 8 taken 1383 times.
✗ Branch 7 → 27 not taken.
✓ Branch 8 → 9 taken 1383 times.
✗ Branch 8 → 25 not taken.
5532 CLI::App *subCmd = app.add_subcommand("install", "Builds your Spice program and installs it to a directory in the PATH");
340
2/4
✓ Branch 15 → 16 taken 1383 times.
✗ Branch 15 → 39 not taken.
✓ Branch 16 → 17 taken 1383 times.
✗ Branch 16 → 37 not taken.
2766 subCmd->alias("i");
341 1383 subCmd->allow_non_standard_option_names();
342 1383 subCmd->callback([&] {
343 2 shouldCompile = true;
344 2 shouldInstall = true;
345 2 ensureNotDockerized();
346 2 });
347
348 1383 addCompileSubcommandOptions(subCmd);
349 1383 }
350
351 /**
352 * Add uninstall subcommand to cli interface
353 */
354 1383 void Driver::addUninstallSubcommand() {
355 // Create sub-command itself
356
3/6
✓ Branch 4 → 5 taken 1383 times.
✗ Branch 4 → 52 not taken.
✓ Branch 7 → 8 taken 1383 times.
✗ Branch 7 → 46 not taken.
✓ Branch 8 → 9 taken 1383 times.
✗ Branch 8 → 44 not taken.
5532 CLI::App *subCmd = app.add_subcommand("uninstall", "Uninstalls a Spice program from the system");
357
2/4
✓ Branch 15 → 16 taken 1383 times.
✗ Branch 15 → 58 not taken.
✓ Branch 16 → 17 taken 1383 times.
✗ Branch 16 → 56 not taken.
2766 subCmd->alias("u");
358 1383 subCmd->allow_non_standard_option_names();
359 1383 subCmd->callback([&] {
360 2 shouldUninstall = true;
361 2 ensureNotDockerized();
362 2 });
363
364 // Source file
365
2/4
✓ Branch 25 → 26 taken 1383 times.
✗ Branch 25 → 79 not taken.
✓ Branch 28 → 29 taken 1383 times.
✗ Branch 28 → 73 not taken.
5532 subCmd->add_option<std::filesystem::path>("<main-source-file>", cliOptions.mainSourceFile, "Main source file")
366
3/6
✓ Branch 29 → 30 taken 1383 times.
✗ Branch 29 → 71 not taken.
✓ Branch 33 → 34 taken 1383 times.
✗ Branch 33 → 64 not taken.
✓ Branch 34 → 35 taken 1383 times.
✗ Branch 34 → 62 not taken.
5532 ->check(CLI::ExistingFile)
367 1383 ->required();
368 1383 }
369
370 5532 void Driver::addCompileSubcommandOptions(CLI::App *subCmd) const {
371 5538 const auto buildModeCallback = [&](const CLI::results_t &results) {
372
1/2
✓ Branch 3 → 4 taken 6 times.
✗ Branch 3 → 26 not taken.
6 std::string inputString = results.front();
373
1/2
✓ Branch 5 → 6 taken 6 times.
✗ Branch 5 → 24 not taken.
6 std::ranges::transform(inputString, inputString.begin(), tolower);
374
375
2/4
✓ Branch 6 → 7 taken 6 times.
✗ Branch 6 → 24 not taken.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 6 times.
6 if (inputString == BUILD_MODE_DEBUG)
376 ✗ cliOptions.buildMode = BuildMode::DEBUG;
377
3/4
✓ Branch 9 → 10 taken 6 times.
✗ Branch 9 → 24 not taken.
✓ Branch 10 → 11 taken 4 times.
✓ Branch 10 → 12 taken 2 times.
6 else if (inputString == BUILD_MODE_RELEASE)
378 4 cliOptions.buildMode = BuildMode::RELEASE;
379
2/4
✓ Branch 12 → 13 taken 2 times.
✗ Branch 12 → 24 not taken.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 2 times.
2 else if (inputString == BUILD_MODE_TEST)
380 ✗ cliOptions.buildMode = BuildMode::TEST;
381 else
382
1/2
✓ Branch 16 → 17 taken 2 times.
✗ Branch 16 → 21 not taken.
2 throw CliError(INVALID_BUILD_MODE, inputString);
383
384 4 return true;
385 6 };
386 5536 const auto buildVarCallback = [&](const std::vector<std::string> &inputs) {
387
2/2
✓ Branch 29 → 4 taken 24 times.
✓ Branch 29 → 30 taken 4 times.
32 for (const std::string &input : inputs) {
388 // Skip empty inputs
389
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 24 times.
24 if (input.empty())
390 ✗ continue;
391
392 // Parse structure: "key=value". If no value given, save "true" as value.
393 24 const size_t splitPos = input.find_first_of('=');
394
2/2
✓ Branch 10 → 11 taken 14 times.
✓ Branch 10 → 18 taken 10 times.
24 if (splitPos != std::string::npos) {
395
1/2
✓ Branch 11 → 12 taken 14 times.
✗ Branch 11 → 36 not taken.
14 const std::string key = input.substr(0, splitPos);
396
1/2
✓ Branch 12 → 13 taken 14 times.
✗ Branch 12 → 34 not taken.
14 const std::string value = input.substr(splitPos + 1);
397
2/4
✓ Branch 13 → 14 taken 14 times.
✗ Branch 13 → 32 not taken.
✓ Branch 14 → 15 taken 14 times.
✗ Branch 14 → 32 not taken.
14 cliOptions.buildVars[key] = value;
398 14 } else {
399
2/4
✓ Branch 18 → 19 taken 10 times.
✗ Branch 18 → 37 not taken.
✓ Branch 19 → 20 taken 10 times.
✗ Branch 19 → 37 not taken.
10 cliOptions.buildVars[input] = "true";
400 }
401 }
402 4 return true;
403 5532 };
404
405 // --build-mode
406
3/6
✓ Branch 5 → 6 taken 5532 times.
✗ Branch 5 → 333 not taken.
✓ Branch 9 → 10 taken 5532 times.
✗ Branch 9 → 324 not taken.
✓ Branch 10 → 11 taken 5532 times.
✗ Branch 10 → 322 not taken.
27660 subCmd->add_option("--build-mode,-m", buildModeCallback, "Build mode: debug (default), release, test");
407 // --llvm-args
408
3/6
✓ Branch 19 → 20 taken 5532 times.
✗ Branch 19 → 348 not taken.
✓ Branch 22 → 23 taken 5532 times.
✗ Branch 22 → 342 not taken.
✓ Branch 23 → 24 taken 5532 times.
✗ Branch 23 → 340 not taken.
22128 subCmd->add_option<std::string>("--llvm-args,-llvm", cliOptions.llvmArgs, "Additional arguments for LLVM")->join(' ');
409 // --jobs
410
2/4
✓ Branch 34 → 35 taken 5532 times.
✗ Branch 34 → 354 not taken.
✓ Branch 35 → 36 taken 5532 times.
✗ Branch 35 → 352 not taken.
16596 subCmd->add_option<unsigned short>("--jobs,-j", cliOptions.compileJobCount,
411
1/2
✓ Branch 31 → 32 taken 5532 times.
✗ Branch 31 → 360 not taken.
11064 "Number of source files to compile in parallel in the back end (0 = auto)");
412 // --build-var
413
1/2
✓ Branch 47 → 48 taken 5532 times.
✗ Branch 47 → 364 not taken.
11064 CLI::Option *buildVarOption = subCmd->add_option_function<std::vector<std::string>>(
414
2/4
✓ Branch 42 → 43 taken 5532 times.
✗ Branch 42 → 375 not taken.
✓ Branch 46 → 47 taken 5532 times.
✗ Branch 46 → 366 not taken.
22128 "--build-var,-b", buildVarCallback, "Add build variable to parametrize the compiled program (e.g. -v key=value)");
415 5532 buildVarOption->multi_option_policy(CLI::MultiOptionPolicy::TakeAll);
416 // --ignore-cache
417
3/6
✓ Branch 56 → 57 taken 5532 times.
✗ Branch 56 → 387 not taken.
✓ Branch 59 → 60 taken 5532 times.
✗ Branch 59 → 381 not taken.
✓ Branch 60 → 61 taken 5532 times.
✗ Branch 60 → 379 not taken.
22128 subCmd->add_flag<bool>("--ignore-cache", cliOptions.ignoreCache, "Force re-compilation of all source files");
418 // --use-lifetime-markers
419
2/4
✓ Branch 70 → 71 taken 5532 times.
✗ Branch 70 → 393 not taken.
✓ Branch 71 → 72 taken 5532 times.
✗ Branch 71 → 391 not taken.
16596 subCmd->add_flag<bool>("--use-lifetime-markers", cliOptions.useLifetimeMarkers,
420
1/2
✓ Branch 67 → 68 taken 5532 times.
✗ Branch 67 → 399 not taken.
11064 "Generate lifetime markers to enhance optimizations");
421 // --use-tbaa-metadata
422
2/4
✓ Branch 81 → 82 taken 5532 times.
✗ Branch 81 → 405 not taken.
✓ Branch 82 → 83 taken 5532 times.
✗ Branch 82 → 403 not taken.
16596 subCmd->add_flag<bool>("--use-tbaa-metadata", cliOptions.useTBAAMetadata,
423
1/2
✓ Branch 78 → 79 taken 5532 times.
✗ Branch 78 → 411 not taken.
11064 "Generate metadata for type-based alias analysis to enhance optimizations");
424 // --keep-frame-pointers
425
2/4
✓ Branch 92 → 93 taken 5532 times.
✗ Branch 92 → 417 not taken.
✓ Branch 93 → 94 taken 5532 times.
✗ Branch 93 → 415 not taken.
16596 subCmd->add_flag<bool>("--keep-frame-pointers", cliOptions.keepFramePointers,
426
1/2
✓ Branch 89 → 90 taken 5532 times.
✗ Branch 89 → 423 not taken.
11064 "Set up a frame pointer in every function, so external profilers and debuggers can walk "
427 "the stack (costs a register and a bit of performance)");
428
429 // Opt levels
430
3/6
✓ Branch 100 → 101 taken 5532 times.
✗ Branch 100 → 439 not taken.
✓ Branch 104 → 105 taken 5532 times.
✗ Branch 104 → 429 not taken.
✓ Branch 105 → 106 taken 5532 times.
✗ Branch 105 → 427 not taken.
22128 subCmd->add_flag_callback("-O0", [&] { cliOptions.optLevel = OptLevel::O0; }, "Disable optimization.");
431
3/6
✓ Branch 113 → 114 taken 5532 times.
✗ Branch 113 → 455 not taken.
✓ Branch 117 → 118 taken 5532 times.
✗ Branch 117 → 445 not taken.
✓ Branch 118 → 119 taken 5532 times.
✗ Branch 118 → 443 not taken.
22128 subCmd->add_flag_callback("-O1", [&] { cliOptions.optLevel = OptLevel::O1; }, "Only basic optimization is applied.");
432
3/6
✓ Branch 126 → 127 taken 5532 times.
✗ Branch 126 → 471 not taken.
✓ Branch 130 → 131 taken 5532 times.
✗ Branch 130 → 461 not taken.
✓ Branch 131 → 132 taken 5532 times.
✗ Branch 131 → 459 not taken.
22128 subCmd->add_flag_callback("-O2", [&] { cliOptions.optLevel = OptLevel::O2; }, "More advanced optimization is applied.");
433
3/6
✓ Branch 139 → 140 taken 5532 times.
✗ Branch 139 → 487 not taken.
✓ Branch 143 → 144 taken 5532 times.
✗ Branch 143 → 477 not taken.
✓ Branch 144 → 145 taken 5532 times.
✗ Branch 144 → 475 not taken.
22128 subCmd->add_flag_callback("-O3", [&] { cliOptions.optLevel = OptLevel::O3; }, "Aggressive optimization for best performance.");
434
3/6
✓ Branch 152 → 153 taken 5532 times.
✗ Branch 152 → 503 not taken.
✓ Branch 156 → 157 taken 5532 times.
✗ Branch 156 → 493 not taken.
✓ Branch 157 → 158 taken 5532 times.
✗ Branch 157 → 491 not taken.
22128 subCmd->add_flag_callback("-Os", [&] { cliOptions.optLevel = OptLevel::Os; }, "Size optimization for output executable.");
435
3/6
✓ Branch 165 → 166 taken 5532 times.
✗ Branch 165 → 519 not taken.
✓ Branch 169 → 170 taken 5532 times.
✗ Branch 169 → 509 not taken.
✓ Branch 170 → 171 taken 5532 times.
✗ Branch 170 → 507 not taken.
22128 subCmd->add_flag_callback("-Oz", [&] { cliOptions.optLevel = OptLevel::Oz; }, "Aggressive optimization for best size.");
436
3/6
✓ Branch 178 → 179 taken 5532 times.
✗ Branch 178 → 531 not taken.
✓ Branch 181 → 182 taken 5532 times.
✗ Branch 181 → 525 not taken.
✓ Branch 182 → 183 taken 5532 times.
✗ Branch 182 → 523 not taken.
22128 subCmd->add_flag<bool>("-lto", cliOptions.useLTO, "Enable link time optimization (LTO)");
437
438 // --backend
439 5545 const auto backendCallback = [&](const CLI::results_t &results) {
440
1/2
✓ Branch 3 → 4 taken 13 times.
✗ Branch 3 → 23 not taken.
13 std::string inputString = results.front();
441
1/2
✓ Branch 5 → 6 taken 13 times.
✗ Branch 5 → 21 not taken.
13 std::ranges::transform(inputString, inputString.begin(), tolower);
442
443
3/4
✓ Branch 6 → 7 taken 13 times.
✗ Branch 6 → 21 not taken.
✓ Branch 7 → 8 taken 2 times.
✓ Branch 7 → 9 taken 11 times.
13 if (inputString == BACKEND_LLVM) {
444 2 cliOptions.backend = Backend::LLVM;
445
3/4
✓ Branch 9 → 10 taken 11 times.
✗ Branch 9 → 21 not taken.
✓ Branch 10 → 11 taken 9 times.
✓ Branch 10 → 12 taken 2 times.
11 } else if (inputString == BACKEND_TPDE) {
446 #ifdef SPICE_ENABLE_TPDE
447 9 cliOptions.backend = Backend::TPDE;
448 #else
449 throw CliError(FEATURE_NOT_SUPPORTED_FOR_TARGET,
450 "The TPDE backend is not available in this build. Rebuild the compiler with "
451 "-DSPICE_ENABLE_TPDE=ON.");
452 #endif
453 } else {
454
1/2
✓ Branch 13 → 14 taken 2 times.
✗ Branch 13 → 18 not taken.
2 throw CliError(INVALID_BACKEND, inputString);
455 }
456
457 11 return true;
458 13 };
459
2/4
✓ Branch 194 → 195 taken 5532 times.
✗ Branch 194 → 537 not taken.
✓ Branch 195 → 196 taken 5532 times.
✗ Branch 195 → 535 not taken.
22128 subCmd->add_option("--backend", backendCallback,
460
1/2
✓ Branch 190 → 191 taken 5532 times.
✗ Branch 190 → 546 not taken.
16596 "Codegen backend: llvm (default), tpde (experimental — fast, unoptimized; requires opt-in build)");
461
462 // --debug-output
463
3/6
✓ Branch 204 → 205 taken 5532 times.
✗ Branch 204 → 561 not taken.
✓ Branch 207 → 208 taken 5532 times.
✗ Branch 207 → 555 not taken.
✓ Branch 208 → 209 taken 5532 times.
✗ Branch 208 → 553 not taken.
22128 subCmd->add_flag<bool>("--debug-output,-d", cliOptions.printDebugOutput, "Enable debug output");
464 // --dump-ast
465
3/6
✓ Branch 215 → 216 taken 5532 times.
✗ Branch 215 → 573 not taken.
✓ Branch 218 → 219 taken 5532 times.
✗ Branch 218 → 567 not taken.
✓ Branch 219 → 220 taken 5532 times.
✗ Branch 219 → 565 not taken.
22128 subCmd->add_flag<bool>("--dump-ast,-ast", cliOptions.dump.dumpAST, "Dump AST as serialized string and SVG image");
466 // --dump-symtab
467
3/6
✓ Branch 226 → 227 taken 5532 times.
✗ Branch 226 → 585 not taken.
✓ Branch 229 → 230 taken 5532 times.
✗ Branch 229 → 579 not taken.
✓ Branch 230 → 231 taken 5532 times.
✗ Branch 230 → 577 not taken.
22128 subCmd->add_flag<bool>("--dump-symtab", cliOptions.dump.dumpSymbolTable, "Dump serialized symbol tables");
468 // --dump-types
469
3/6
✓ Branch 237 → 238 taken 5532 times.
✗ Branch 237 → 597 not taken.
✓ Branch 240 → 241 taken 5532 times.
✗ Branch 240 → 591 not taken.
✓ Branch 241 → 242 taken 5532 times.
✗ Branch 241 → 589 not taken.
22128 subCmd->add_flag<bool>("--dump-types", cliOptions.dump.dumpTypes, "Dump all used types");
470 // --dump-cache-stats
471
3/6
✓ Branch 248 → 249 taken 5532 times.
✗ Branch 248 → 609 not taken.
✓ Branch 251 → 252 taken 5532 times.
✗ Branch 251 → 603 not taken.
✓ Branch 252 → 253 taken 5532 times.
✗ Branch 252 → 601 not taken.
22128 subCmd->add_flag<bool>("--dump-cache-stats", cliOptions.dump.dumpCacheStats, "Dump stats for compiler-internal lookup caches");
472 // --dump-ir
473
3/6
✓ Branch 259 → 260 taken 5532 times.
✗ Branch 259 → 621 not taken.
✓ Branch 262 → 263 taken 5532 times.
✗ Branch 262 → 615 not taken.
✓ Branch 263 → 264 taken 5532 times.
✗ Branch 263 → 613 not taken.
22128 subCmd->add_flag<bool>("--dump-ir,-ir", cliOptions.dump.dumpIR, "Dump LLVM-IR");
474 // --dump-assembly
475
3/6
✓ Branch 270 → 271 taken 5532 times.
✗ Branch 270 → 633 not taken.
✓ Branch 273 → 274 taken 5532 times.
✗ Branch 273 → 627 not taken.
✓ Branch 274 → 275 taken 5532 times.
✗ Branch 274 → 625 not taken.
22128 subCmd->add_flag<bool>("--dump-assembly,-asm,-s", cliOptions.dump.dumpAssembly, "Dump Assembly code");
476 // --dump-object-file
477
3/6
✓ Branch 281 → 282 taken 5532 times.
✗ Branch 281 → 645 not taken.
✓ Branch 284 → 285 taken 5532 times.
✗ Branch 284 → 639 not taken.
✓ Branch 285 → 286 taken 5532 times.
✗ Branch 285 → 637 not taken.
22128 subCmd->add_flag<bool>("--dump-object-file", cliOptions.dump.dumpObjectFiles, "Dump object files");
478 // --dump-dependency-graph
479
3/6
✓ Branch 292 → 293 taken 5532 times.
✗ Branch 292 → 657 not taken.
✓ Branch 295 → 296 taken 5532 times.
✗ Branch 295 → 651 not taken.
✓ Branch 296 → 297 taken 5532 times.
✗ Branch 296 → 649 not taken.
22128 subCmd->add_flag<bool>("--dump-dependency-graph", cliOptions.dump.dumpDependencyGraph, "Dump compile unit dependency graph");
480
481 // Source file
482
2/4
✓ Branch 303 → 304 taken 5532 times.
✗ Branch 303 → 678 not taken.
✓ Branch 306 → 307 taken 5532 times.
✗ Branch 306 → 672 not taken.
22128 subCmd->add_option<std::filesystem::path>("<main-source-file>", cliOptions.mainSourceFile, "Main source file")
483
3/6
✓ Branch 307 → 308 taken 5532 times.
✗ Branch 307 → 670 not taken.
✓ Branch 311 → 312 taken 5532 times.
✗ Branch 311 → 663 not taken.
✓ Branch 312 → 313 taken 5532 times.
✗ Branch 312 → 661 not taken.
22128 ->check(CLI::ExistingFile)
484 5532 ->required();
485 5532 }
486
487 4149 void Driver::addInstrumentationOptions(CLI::App *subCmd) {
488 4177 const auto debugInfoCallback = [&](const CLI::results_t &results) {
489
1/2
✓ Branch 3 → 4 taken 28 times.
✗ Branch 3 → 36 not taken.
28 std::string inputString = results.front();
490
1/2
✓ Branch 5 → 6 taken 28 times.
✗ Branch 5 → 34 not taken.
28 std::ranges::transform(inputString, inputString.begin(), tolower);
491
492 28 debugInfoLevelSetExplicitly = true;
493
494 // CLI11 reports a flag without an attached value as 'true', so a bare '-g' means full debug info
495
8/10
✓ Branch 6 → 7 taken 28 times.
✗ Branch 6 → 34 not taken.
✓ Branch 7 → 8 taken 24 times.
✓ Branch 7 → 10 taken 4 times.
✓ Branch 8 → 9 taken 24 times.
✗ Branch 8 → 34 not taken.
✓ Branch 9 → 10 taken 6 times.
✓ Branch 9 → 11 taken 18 times.
✓ Branch 12 → 13 taken 10 times.
✓ Branch 12 → 14 taken 18 times.
28 if (inputString == "true" || inputString == DEBUG_INFO_FULL)
496 10 cliOptions.instrumentation.debugInfoLevel = DebugInfoLevel::FULL;
497
3/4
✓ Branch 14 → 15 taken 18 times.
✗ Branch 14 → 34 not taken.
✓ Branch 15 → 16 taken 10 times.
✓ Branch 15 → 17 taken 8 times.
18 else if (inputString == DEBUG_INFO_LINE_ONLY)
498 10 cliOptions.instrumentation.debugInfoLevel = DebugInfoLevel::LINE_ONLY;
499
7/10
✓ Branch 17 → 18 taken 8 times.
✗ Branch 17 → 34 not taken.
✓ Branch 18 → 19 taken 8 times.
✗ Branch 18 → 21 not taken.
✓ Branch 19 → 20 taken 8 times.
✗ Branch 19 → 34 not taken.
✓ Branch 20 → 21 taken 6 times.
✓ Branch 20 → 22 taken 2 times.
✓ Branch 23 → 24 taken 6 times.
✓ Branch 23 → 25 taken 2 times.
8 else if (inputString == "false" || inputString == DEBUG_INFO_NONE)
500 6 cliOptions.instrumentation.debugInfoLevel = DebugInfoLevel::NONE;
501 else
502
1/2
✓ Branch 26 → 27 taken 2 times.
✗ Branch 26 → 31 not taken.
2 throw CliError(INVALID_DEBUG_INFO_LEVEL, inputString);
503
504 26 return true;
505 28 };
506
507 4169 const auto debugInfoAliasCallback = [&] {
508 20 cliOptions.instrumentation.debugInfoLevel = DebugInfoLevel::FULL;
509 20 debugInfoLevelSetExplicitly = true;
510 4169 };
511
512 4172 const auto sanitizerCallback = [&](const CLI::results_t &results) {
513
1/2
✓ Branch 3 → 4 taken 23 times.
✗ Branch 3 → 32 not taken.
23 std::string inputString = results.front();
514
1/2
✓ Branch 5 → 6 taken 23 times.
✗ Branch 5 → 30 not taken.
23 std::ranges::transform(inputString, inputString.begin(), tolower);
515
516
2/4
✓ Branch 6 → 7 taken 23 times.
✗ Branch 6 → 30 not taken.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 23 times.
23 if (inputString == SANITIZER_NONE)
517 ✗ cliOptions.instrumentation.sanitizer = Sanitizer::NONE;
518
3/4
✓ Branch 9 → 10 taken 23 times.
✗ Branch 9 → 30 not taken.
✓ Branch 10 → 11 taken 11 times.
✓ Branch 10 → 12 taken 12 times.
23 else if (inputString == SANITIZER_ADDRESS)
519 11 cliOptions.instrumentation.sanitizer = Sanitizer::ADDRESS;
520
3/4
✓ Branch 12 → 13 taken 12 times.
✗ Branch 12 → 30 not taken.
✓ Branch 13 → 14 taken 4 times.
✓ Branch 13 → 15 taken 8 times.
12 else if (inputString == SANITIZER_THREAD)
521 4 cliOptions.instrumentation.sanitizer = Sanitizer::THREAD;
522
3/4
✓ Branch 15 → 16 taken 8 times.
✗ Branch 15 → 30 not taken.
✓ Branch 16 → 17 taken 4 times.
✓ Branch 16 → 18 taken 4 times.
8 else if (inputString == SANITIZER_MEMORY)
523 4 cliOptions.instrumentation.sanitizer = Sanitizer::MEMORY;
524
3/4
✓ Branch 18 → 19 taken 4 times.
✗ Branch 18 → 30 not taken.
✓ Branch 19 → 20 taken 2 times.
✓ Branch 19 → 21 taken 2 times.
4 else if (inputString == SANITIZER_TYPE)
525 2 cliOptions.instrumentation.sanitizer = Sanitizer::TYPE;
526 else
527
1/2
✓ Branch 22 → 23 taken 2 times.
✗ Branch 22 → 27 not taken.
2 throw CliError(INVALID_SANITIZER, inputString);
528
529 21 return true;
530 23 };
531
532 // --debug-info
533 // Registered as an option with zero expected arguments, which makes CLI11 treat it like a flag. That way a bare
534 // '--debug-info' keeps working, while the level can optionally be attached with '=' (e.g. '--debug-info=line-only').
535 // A flag never consumes the following argument, so the main source file cannot be swallowed by a bare '--debug-info'.
536 // CLI11 runs option callbacks in registration order, so both this option and its '-g' alias below are triggered on
537 // parse instead. That way the one that comes last on the command line wins, no matter which one it is.
538
2/4
✓ Branch 5 → 6 taken 4149 times.
✗ Branch 5 → 72 not taken.
✓ Branch 9 → 10 taken 4149 times.
✗ Branch 9 → 63 not taken.
20745 subCmd->add_option("--debug-info", debugInfoCallback, "Generate debug info: full (default), line-only, none")
539 ->expected(0)
540 ->multi_option_policy(CLI::MultiOptionPolicy::TakeLast)
541
1/2
✓ Branch 10 → 11 taken 4149 times.
✗ Branch 10 → 61 not taken.
8298 ->trigger_on_parse();
542 // -g
543 // CLI11 only supports the '<option>=<value>' syntax for long option names, so '-g' cannot carry a level itself
544
3/6
✓ Branch 22 → 23 taken 4149 times.
✗ Branch 22 → 90 not taken.
✓ Branch 26 → 27 taken 4149 times.
✗ Branch 26 → 81 not taken.
✓ Branch 27 → 28 taken 4149 times.
✗ Branch 27 → 79 not taken.
16596 subCmd->add_flag_callback("-g", debugInfoAliasCallback, "Alias for --debug-info=full")->trigger_on_parse();
545 // --sanitizer
546
3/6
✓ Branch 37 → 38 taken 4149 times.
✗ Branch 37 → 105 not taken.
✓ Branch 41 → 42 taken 4149 times.
✗ Branch 41 → 96 not taken.
✓ Branch 42 → 43 taken 4149 times.
✗ Branch 42 → 94 not taken.
20745 subCmd->add_option("--sanitizer", sanitizerCallback, "Enable sanitizer: none (default), address, thread, memory, type");
547 // --coverage
548
2/4
✓ Branch 54 → 55 taken 4149 times.
✗ Branch 54 → 114 not taken.
✓ Branch 55 → 56 taken 4149 times.
✗ Branch 55 → 112 not taken.
12447 subCmd->add_flag<bool>("--coverage", cliOptions.instrumentation.codeCoverage,
549
1/2
✓ Branch 51 → 52 taken 4149 times.
✗ Branch 51 → 120 not taken.
8298 "Instrument code for coverage analysis (gcov-compatible .gcno/.gcda output)");
550 4149 }
551
552 /**
553 * Ensure that the compiler is not running in a Docker container
554 */
555 4 void Driver::ensureNotDockerized() {
556 4 const char *envValue = std::getenv(ENV_VAR_DOCKERIZED);
557 − if (envValue != nullptr && std::strcmp(envValue, "true") == 0) { // LCOV_EXCL_START
558 − const auto msg = "This feature is not supported in a containerized environment. Please use the standalone version of Spice.";
559 − throw CliError(FEATURE_NOT_SUPPORTED_WHEN_DOCKERIZED, msg);
560 } // LCOV_EXCL_STOP
561 4 }
562
563 } // namespace spice::compiler
564