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 | 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 | 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 | 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 |