GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 90.5% 314 / 3 / 350
Functions: 85.3% 29 / 0 / 34
Branches: 50.8% 426 / 8 / 846

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