GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 100.0% 74 / 9 / 83
Functions: 100.0% 9 / 0 / 9
Branches: 57.1% 56 / 36 / 134

src/iroptimizer/IROptimizer.cpp
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 #include "IROptimizer.h"
4
5 #include <SourceFile.h>
6 #include <global/GlobalResourceManager.h>
7 #include <driver/Driver.h>
8
9 #include <llvm/Analysis/ModuleSummaryAnalysis.h>
10 #include <llvm/Transforms/Instrumentation/AddressSanitizer.h>
11 #include <llvm/Transforms/Instrumentation/GCOVProfiler.h>
12 #include <llvm/Transforms/Instrumentation/MemorySanitizer.h>
13 #include <llvm/Transforms/Instrumentation/ThreadSanitizer.h>
14 #include <llvm/Transforms/Instrumentation/TypeSanitizer.h>
15 #include <llvm/Analysis/AliasAnalysis.h>
16
17 #include "CoverageWorkingDirFileSystem.h"
18
19 namespace spice::compiler {
20
21 5311 IROptimizer::IROptimizer(GlobalResourceManager &resourceManager, SourceFile *sourceFile)
22 : CompilerPass(resourceManager, sourceFile),
23
3/4
✓ Branch 7 → 8 taken 4 times.
✓ Branch 7 → 9 taken 5307 times.
✓ Branch 10 → 11 taken 5311 times.
✗ Branch 10 → 16 not taken.
5311 si(cliOptions.useLTO ? resourceManager.ltoContext : sourceFile->context, false, resourceManager.cliOptions.testMode,
24
5/10
✓ Branch 3 → 4 taken 5311 times.
✗ Branch 3 → 24 not taken.
✓ Branch 4 → 5 taken 5311 times.
✗ Branch 4 → 22 not taken.
✓ Branch 5 → 6 taken 5311 times.
✗ Branch 5 → 20 not taken.
✓ Branch 6 → 7 taken 5311 times.
✗ Branch 6 → 18 not taken.
✓ Branch 11 → 12 taken 5311 times.
✗ Branch 11 → 14 not taken.
10622 llvm::PrintPassOptions(false, true, false)) {}
25
26 5311 void IROptimizer::prepare() {
27
1/2
✓ Branch 2 → 3 taken 5311 times.
✗ Branch 2 → 25 not taken.
5311 llvm::PipelineTuningOptions pto;
28
2/2
✓ Branch 3 → 4 taken 6 times.
✓ Branch 3 → 5 taken 5305 times.
5311 if (!resourceManager.cliOptions.testMode)
29
1/2
✓ Branch 4 → 5 taken 6 times.
✗ Branch 4 → 25 not taken.
6 si.registerCallbacks(pic, &moduleAnalysisMgr);
30
1/2
✓ Branch 6 → 7 taken 5311 times.
✗ Branch 6 → 21 not taken.
5311 passBuilder = std::make_unique<llvm::PassBuilder>(sourceFile->targetMachine.get(), pto, std::nullopt, &pic);
31
32
1/2
✓ Branch 9 → 10 taken 5311 times.
✗ Branch 9 → 24 not taken.
10622 functionAnalysisMgr.registerPass([&] { return passBuilder->buildDefaultAAPipeline(); });
33
34
1/2
✓ Branch 11 → 12 taken 5311 times.
✗ Branch 11 → 25 not taken.
5311 passBuilder->registerModuleAnalyses(moduleAnalysisMgr);
35
1/2
✓ Branch 13 → 14 taken 5311 times.
✗ Branch 13 → 25 not taken.
5311 passBuilder->registerCGSCCAnalyses(cgsccAnalysisMgr);
36
1/2
✓ Branch 15 → 16 taken 5311 times.
✗ Branch 15 → 25 not taken.
5311 passBuilder->registerFunctionAnalyses(functionAnalysisMgr);
37
1/2
✓ Branch 17 → 18 taken 5311 times.
✗ Branch 17 → 25 not taken.
5311 passBuilder->registerLoopAnalyses(loopAnalysisMgr);
38
1/2
✓ Branch 19 → 20 taken 5311 times.
✗ Branch 19 → 25 not taken.
5311 passBuilder->crossRegisterProxies(loopAnalysisMgr, functionAnalysisMgr, cgsccAnalysisMgr, moduleAnalysisMgr);
39 5311 }
40
41 5307 void IROptimizer::optimizeDefault() {
42 if (cliOptions.printDebugOutput && cliOptions.dump.dumpIR && !cliOptions.dump.dumpToFiles) // GCOV_EXCL_LINE
43 std::cout << "\nOptimizing on level " + std::to_string(static_cast<uint8_t>(cliOptions.optLevel)) // GCOV_EXCL_LINE
44 << " ...\n"; // GCOV_EXCL_LINE
45
46 // Prepare pipeline
47 5307 const llvm::OptimizationLevel llvmOptLevel = getLLVMOptLevelFromSpiceOptLevel();
48
1/2
✓ Branch 14 → 15 taken 5307 times.
✗ Branch 14 → 31 not taken.
5307 llvm::ModulePassManager modulePassMgr = passBuilder->buildPerModuleDefaultPipeline(llvmOptLevel);
49
50 // Add optional passes
51
1/2
✓ Branch 15 → 16 taken 5307 times.
✗ Branch 15 → 29 not taken.
5307 addInstrumentationPassToPipeline(modulePassMgr);
52
1/2
✓ Branch 16 → 17 taken 5307 times.
✗ Branch 16 → 29 not taken.
5307 addCoveragePassToPipeline(modulePassMgr);
53
54 // Run pipeline
55
1/2
✓ Branch 18 → 19 taken 5307 times.
✗ Branch 18 → 28 not taken.
5307 modulePassMgr.run(*sourceFile->llvmModule, moduleAnalysisMgr);
56 5307 }
57
58 3 void IROptimizer::optimizePreLink() {
59 if (cliOptions.printDebugOutput && cliOptions.dump.dumpIR && !cliOptions.dump.dumpToFiles) // GCOV_EXCL_LINE
60 std::cout << "\nOptimizing on level " + std::to_string(static_cast<uint8_t>(cliOptions.optLevel)) // GCOV_EXCL_LINE
61 << " (pre-link) ...\n"; // GCOV_EXCL_LINE
62
63 // Prepare pipeline
64 3 const llvm::OptimizationLevel llvmOptLevel = getLLVMOptLevelFromSpiceOptLevel();
65
1/2
✓ Branch 14 → 15 taken 3 times.
✗ Branch 14 → 33 not taken.
3 llvm::ModulePassManager modulePassMgr = passBuilder->buildLTOPreLinkDefaultPipeline(llvmOptLevel);
66
67 // Run pipeline
68
1/2
✓ Branch 16 → 17 taken 3 times.
✗ Branch 16 → 29 not taken.
3 modulePassMgr.run(*sourceFile->llvmModule, moduleAnalysisMgr);
69
70 // Generate module summary index
71 llvm::ModuleSummaryIndexAnalysis moduleSummaryIndexAnalysis;
72
1/2
✓ Branch 19 → 20 taken 3 times.
✗ Branch 19 → 30 not taken.
3 moduleSummaryIndexAnalysis.run(*sourceFile->llvmModule, moduleAnalysisMgr);
73 3 }
74
75 1 void IROptimizer::optimizePostLink() {
76 if (cliOptions.printDebugOutput && cliOptions.dump.dumpIR && !cliOptions.dump.dumpToFiles) // GCOV_EXCL_LINE
77 std::cout << "\nOptimizing on level " + std::to_string(static_cast<uint8_t>(cliOptions.optLevel)) // GCOV_EXCL_LINE
78 << " (post-link) ...\n"; // GCOV_EXCL_LINE
79 1 llvm::Module &ltoModule = *resourceManager.ltoModule;
80
81 // Compute module summary index
82 llvm::ModuleSummaryIndexAnalysis moduleSummaryIndexAnalysis;
83
1/2
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 35 not taken.
1 llvm::ModuleSummaryIndex moduleSummaryIndex = moduleSummaryIndexAnalysis.run(ltoModule, moduleAnalysisMgr);
84 1 moduleSummaryIndex.setWithWholeProgramVisibility();
85
86 // Prepare pipeline
87 1 const llvm::OptimizationLevel llvmOptLevel = getLLVMOptLevelFromSpiceOptLevel();
88
1/2
✓ Branch 17 → 18 taken 1 time.
✗ Branch 17 → 33 not taken.
1 llvm::ModulePassManager modulePassMgr = passBuilder->buildLTODefaultPipeline(llvmOptLevel, &moduleSummaryIndex);
89
90 // Add optional passes
91
1/2
✓ Branch 18 → 19 taken 1 time.
✗ Branch 18 → 31 not taken.
1 addInstrumentationPassToPipeline(modulePassMgr);
92
93 // Run pipeline
94
1/2
✓ Branch 19 → 20 taken 1 time.
✗ Branch 19 → 30 not taken.
1 modulePassMgr.run(ltoModule, moduleAnalysisMgr);
95 1 }
96
97 5308 void IROptimizer::addInstrumentationPassToPipeline(llvm::ModulePassManager &modulePassMgr) const {
98
5/6
✓ Branch 2 → 3 taken 5246 times.
✓ Branch 2 → 4 taken 44 times.
✓ Branch 2 → 8 taken 14 times.
✓ Branch 2 → 13 taken 2 times.
✓ Branch 2 → 18 taken 2 times.
✗ Branch 2 → 20 not taken.
5308 switch (cliOptions.instrumentation.sanitizer) {
99 5246 case Sanitizer::NONE: {
100 5246 return;
101 }
102 44 case Sanitizer::ADDRESS: {
103 44 llvm::AddressSanitizerOptions asanOptions;
104 44 asanOptions.UseAfterScope = true;
105
2/4
✓ Branch 4 → 5 taken 44 times.
✗ Branch 4 → 21 not taken.
✓ Branch 5 → 6 taken 44 times.
✗ Branch 5 → 21 not taken.
44 modulePassMgr.addPass(llvm::AddressSanitizerPass(asanOptions));
106 44 break;
107 }
108 14 case Sanitizer::THREAD: {
109
1/2
✓ Branch 8 → 9 taken 14 times.
✗ Branch 8 → 23 not taken.
14 modulePassMgr.addPass(llvm::ModuleThreadSanitizerPass());
110
2/4
✓ Branch 9 → 10 taken 14 times.
✗ Branch 9 → 26 not taken.
✓ Branch 10 → 11 taken 14 times.
✗ Branch 10 → 24 not taken.
14 modulePassMgr.addPass(llvm::createModuleToFunctionPassAdaptor(llvm::ThreadSanitizerPass()));
111 14 break;
112 }
113 2 case Sanitizer::MEMORY: {
114
1/2
✓ Branch 13 → 14 taken 2 times.
✗ Branch 13 → 29 not taken.
2 llvm::MemorySanitizerOptions msanOptions;
115 2 msanOptions.EagerChecks = true;
116
1/2
✓ Branch 15 → 16 taken 2 times.
✗ Branch 15 → 28 not taken.
2 modulePassMgr.addPass(llvm::MemorySanitizerPass(msanOptions));
117 2 break;
118 }
119 2 case Sanitizer::TYPE: {
120
1/2
✓ Branch 18 → 19 taken 2 times.
✗ Branch 18 → 30 not taken.
2 modulePassMgr.addPass(llvm::TypeSanitizerPass());
121 2 break;
122 }
123 }
124 }
125
126 5307 void IROptimizer::addCoveragePassToPipeline(llvm::ModulePassManager &modulePassMgr) const {
127
2/2
✓ Branch 2 → 3 taken 2568 times.
✓ Branch 2 → 4 taken 2739 times.
5307 if (!cliOptions.instrumentation.codeCoverage)
128 2568 return;
129
3/6
✓ Branch 4 → 5 taken 2739 times.
✗ Branch 4 → 23 not taken.
✓ Branch 5 → 6 taken 2739 times.
✗ Branch 5 → 21 not taken.
✓ Branch 6 → 7 taken 2739 times.
✗ Branch 6 → 19 not taken.
2739 const auto vfs = createFixedWorkingDirFileSystem(absolute(cliOptions.outputDir).string());
130
4/8
✓ Branch 9 → 10 taken 2739 times.
✗ Branch 9 → 32 not taken.
✓ Branch 10 → 11 taken 2739 times.
✗ Branch 10 → 29 not taken.
✓ Branch 11 → 12 taken 2739 times.
✗ Branch 11 → 27 not taken.
✓ Branch 12 → 13 taken 2739 times.
✗ Branch 12 → 25 not taken.
2739 modulePassMgr.addPass(llvm::GCOVProfilerPass(llvm::GCOVOptions::getDefault(), vfs));
131 2739 }
132
133 5311 llvm::OptimizationLevel IROptimizer::getLLVMOptLevelFromSpiceOptLevel() const {
134
4/4
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 4 taken 67 times.
✓ Branch 2 → 5 taken 10 times.
✓ Branch 2 → 6 taken 5232 times.
5311 switch (cliOptions.optLevel) {
135 2 case OptLevel::O1:
136 2 return llvm::OptimizationLevel::O1;
137 67 case OptLevel::Os: // fallthrough - Os = O2 + optsize function attribute
138 case OptLevel::Oz: // fallthrough - Oz = O2 + optsize + minsize function attributes
139 case OptLevel::O2:
140 67 return llvm::OptimizationLevel::O2;
141 10 case OptLevel::O3:
142 10 return llvm::OptimizationLevel::O3;
143 5232 default:
144 5232 return llvm::OptimizationLevel::O0;
145 }
146 }
147
148 } // namespace spice::compiler
149