GCC Code Coverage Report


src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
51 / 54, 13 excl
94.4%
Branches:
10 / 12, 20 excl
83.3%
Functions:
10 / 10
100.0%

iroptimizer/ir-optimizer.spice
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 // Std imports
4 import "std/text/print";
5 import "std/type/type-conversion";
6 import "std/bindings/llvm/llvm" as llvm;
7
8 // Own imports
9 import "@/compiler-pass";
10 import "@/driver";
11 import "@/source-file";
12 import "@/global/global-resource-manager";
13
14 /**
15 * Runs the LLVM optimization pipelines on the modules of the source files.
16 *
17 * The LLVM C API only allows to run a textual pass pipeline, which sets up an llvm::PassBuilder with its analysis managers
18 * internally. So the pipelines are described textually here, e.g. the pipeline of buildPerModuleDefaultPipeline(O2) is
19 * 'default<O2>'.
20 */
21 3501 public type IROptimizer struct {
22 compose public CompilerPass compilerPass
23 llvm::PassBuilderOptions passBuilderOptions
24 }
25
26 3501 public p IROptimizer.ctor(GlobalResourceManager& resourceManager, SourceFile* sourceFile) {
27 3501 this.compilerPass = CompilerPass(resourceManager, sourceFile);
28 3501 }
29
30 3501 public p IROptimizer.prepare() {
31 // Verify the IR after each pass in test mode
32 3501 this.passBuilderOptions.setVerifyEach(this.resourceManager.cliOptions.testMode);
33 3501 }
34
35 3495 public p IROptimizer.optimizeDefault() {
36 − if this.cliOptions.printDebugOutput && this.cliOptions.dump.dumpIR && !this.cliOptions.dump.dumpToFiles { // LCOV_EXCL_LINE
37 − println("\nOptimizing on level " + toString(this.cliOptions.optLevel) + " ..."); // LCOV_EXCL_LINE
38 − } // LCOV_EXCL_LINE
39
40 // Prepare pipeline
41 3495 llvm::PassBuilder passBuilder = llvm::PassBuilder(this.passBuilderOptions);
42 3495 passBuilder.addPass("default<" + this.getLLVMOptLevelName() + ">");
43
44 // Add optional passes
45 3495 this.addInstrumentationPassToPipeline(passBuilder);
46 3495 this.addCoveragePassToPipeline(passBuilder);
47
48 // Run pipeline
49 3495 this.runPipeline(passBuilder, this.sourceFile.llvmModule);
50 3495 }
51
52 4 public p IROptimizer.optimizePreLink() {
53 − if this.cliOptions.printDebugOutput && this.cliOptions.dump.dumpIR && !this.cliOptions.dump.dumpToFiles { // LCOV_EXCL_LINE
54 − println("\nOptimizing on level " + toString(this.cliOptions.optLevel) + " (pre-link) ..."); // LCOV_EXCL_LINE
55 − } // LCOV_EXCL_LINE
56
57 // Prepare pipeline
58 4 llvm::PassBuilder passBuilder = llvm::PassBuilder(this.passBuilderOptions);
59 4 passBuilder.addPass("lto-pre-link<" + this.getLLVMOptLevelName() + ">");
60
61 // Run pipeline
62 // Note: The LLVM C API does not offer to generate the module summary index here
63 4 this.runPipeline(passBuilder, this.sourceFile.llvmModule);
64 4 }
65
66 2 public p IROptimizer.optimizePostLink() {
67 − if this.cliOptions.printDebugOutput && this.cliOptions.dump.dumpIR && !this.cliOptions.dump.dumpToFiles { // LCOV_EXCL_LINE
68 − println("\nOptimizing on level " + toString(this.cliOptions.optLevel) + " (post-link) ..."); // LCOV_EXCL_LINE
69 − } // LCOV_EXCL_LINE
70
71 // Prepare pipeline
72 // Note: The LLVM C API does not offer to pass a module summary index with whole program visibility
73 2 llvm::PassBuilder passBuilder = llvm::PassBuilder(this.passBuilderOptions);
74 2 passBuilder.addPass("lto<" + this.getLLVMOptLevelName() + ">");
75
76 // Add optional passes
77 2 this.addInstrumentationPassToPipeline(passBuilder);
78
79 // Run pipeline
80 2 this.runPipeline(passBuilder, this.resourceManager.ltoModule);
81 2 }
82
83 3501 p IROptimizer.runPipeline(llvm::PassBuilder& passBuilder, llvm::Module& module) {
84 3501 passBuilder.buildCustomPipeline();
85 3501 llvm::LLVMError error = passBuilder.run(module, this.sourceFile.targetMachine);
86 − if error.isError() { // LCOV_EXCL_LINE
87 − const llvm::Message errorMessage = error.getMessage(); // LCOV_EXCL_LINE
88 − panic(Error(errorMessage.get())); // LCOV_EXCL_LINE
89 − } // LCOV_EXCL_LINE
90 3501 }
91
92 3497 p IROptimizer.addInstrumentationPassToPipeline(llvm::PassBuilder& passBuilder) {
93
5/6
✓ Branch 0 taken 3358 times.
✓ Branch 1 taken 103 times.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
3497 switch this.cliOptions.instrumentation.sanitizer {
94 case Sanitizer::NONE: {
95 3358 return;
96 }
97 case Sanitizer::ADDRESS: {
98 // AddressSanitizerPass with UseAfterScope enabled
99 103 passBuilder.addPass("asan<use-after-scope>");
100 103 }
101 case Sanitizer::THREAD: {
102 28 passBuilder.addPass("tsan-module");
103 28 passBuilder.addPass("function(tsan)");
104 28 }
105 case Sanitizer::MEMORY: {
106 // MemorySanitizerPass with EagerChecks enabled
107 4 passBuilder.addPass("msan<eager-checks>");
108 4 }
109 case Sanitizer::TYPE: {
110 4 passBuilder.addPass("tysan");
111 4 }
112 }
113 139 }
114
115 3495 p IROptimizer.addCoveragePassToPipeline(llvm::PassBuilder& passBuilder) {
116
1/2
✓ Branch 0 taken 3495 times.
✗ Branch 1 not taken.
3495 if !this.cliOptions.instrumentation.codeCoverage {
117 3495 return;
118 }
119 // Note: The LLVM C API does not offer to pass a virtual file system with the output dir as working dir
120 ✗ passBuilder.addPass("insert-gcov-profiling");
121 ✗ }
122
123 // Get the name of the LLVM optimization level for the pass pipeline
124 3501 f<string> IROptimizer.getLLVMOptLevelName() {
125
4/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3403 times.
✓ Branch 2 taken 88 times.
✓ Branch 3 taken 9 times.
3501 switch this.cliOptions.optLevel {
126 case OptLevel::O1: {
127 1 return "O1";
128 }
129 case OptLevel::Os, OptLevel::Oz, OptLevel::O2: { // Os = O2 + optsize, Oz = O2 + optsize + minsize function attributes
130 88 return "O2";
131 }
132 case OptLevel::O3: {
133 9 return "O3";
134 }
135 default: {
136 3403 return "O0";
137 }
138 }
139 ✗ }
140