diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OptimizationPipeline.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OptimizationPipeline.kt index 1197553d151..db8bd93e1f1 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OptimizationPipeline.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OptimizationPipeline.kt @@ -8,23 +8,6 @@ import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.konan.target.* import java.io.Closeable -private fun initializeLlvmGlobalPassRegistry() { - val passRegistry = LLVMGetGlobalPassRegistry() - - LLVMInitializeCore(passRegistry) - LLVMInitializeTransformUtils(passRegistry) - LLVMInitializeScalarOpts(passRegistry) - LLVMInitializeVectorization(passRegistry) - LLVMInitializeInstCombine(passRegistry) - LLVMInitializeIPO(passRegistry) - LLVMInitializeInstrumentation(passRegistry) - LLVMInitializeAnalysis(passRegistry) - LLVMInitializeIPA(passRegistry) - LLVMInitializeCodeGen(passRegistry) - LLVMInitializeTarget(passRegistry) - LLVMInitializeObjCARCOpts(passRegistry) -} - enum class LlvmOptimizationLevel(val value: Int) { NONE(0), DEFAULT(1), @@ -54,7 +37,6 @@ data class LlvmPipelineConfig( val makeDeclarationsHidden: Boolean, val objCPasses: Boolean, val inlineThreshold: Int?, - val sanitizer: SanitizerKind?, ) private fun getCpuModel(context: PhaseContext): String { @@ -104,7 +86,6 @@ internal fun createLTOPipelineConfigForRuntime(generationState: NativeGeneration objCPasses = configurables is AppleConfigurables, makeDeclarationsHidden = false, inlineThreshold = tryGetInlineThreshold(generationState), - sanitizer = null ) } @@ -180,24 +161,22 @@ internal fun createLTOFinalPipelineConfig( makeDeclarationsHidden, objcPasses, inlineThreshold, - config.sanitizer ) } /** - * Prepares and executes LLVM LTO pipeline on the given [llvmModule]. - * - * Note: this class is intentionally uncoupled from [Context]. - * Please avoid depending on it. + * Prepares and executes LLVM pipeline on the given [llvmModule]. */ -class LlvmOptimizationPipeline( +abstract class LlvmOptimizationPipeline( private val config: LlvmPipelineConfig, - private val llvmModule: LLVMModuleRef, private val logger: LoggingContext? = null ) : Closeable { - private val arena = Arena() + abstract fun configurePipeline(config: LlvmPipelineConfig, manager: LLVMPassManagerRef, builder: LLVMPassManagerBuilderRef) + open fun executeCustomPreprocessing(config: LlvmPipelineConfig, module: LLVMModuleRef) {} + abstract val pipelineName: String - private val targetMachine: LLVMTargetMachineRef by lazy { + private val arena = Arena() + private val targetMachineDelegate = lazy { val target = arena.alloc() val foundLlvmTarget = LLVMGetTargetFromTriple(config.targetTriple, target.ptr, null) == 0 check(foundLlvmTarget) { "Cannot get target from triple ${config.targetTriple}." } @@ -211,98 +190,157 @@ class LlvmOptimizationPipeline( config.codeModel)!! } - private val modulePasses = LLVMCreatePassManager() - private val passBuilder = LLVMPassManagerBuilderCreate() + private val targetMachine: LLVMTargetMachineRef by targetMachineDelegate - private fun init() { - initLLVMTargets() - initializeLlvmGlobalPassRegistry() - } - private fun populatePasses() { - LLVMPassManagerBuilderSetOptLevel(passBuilder, config.optimizationLevel.value) - LLVMPassManagerBuilderSetSizeLevel(passBuilder, config.sizeLevel.value) - LLVMKotlinAddTargetLibraryInfoWrapperPass(modulePasses, config.targetTriple) - // TargetTransformInfo pass. - LLVMAddAnalysisPasses(targetMachine, modulePasses) - if (config.internalize) { - LLVMAddInternalizePass(modulePasses, 0) - } - if (config.makeDeclarationsHidden) { - makeVisibilityHiddenLikeLlvmInternalizePass(llvmModule) - } - if (config.globalDce) { - LLVMAddGlobalDCEPass(modulePasses) - } - config.inlineThreshold?.let { threshold -> - LLVMPassManagerBuilderUseInlinerWithThreshold(passBuilder, threshold) - } - - // Pipeline that is similar to `llvm-lto`. - LLVMPassManagerBuilderPopulateLTOPassManager(passBuilder, modulePasses, Internalize = 0, RunInliner = 1) - - if (config.objCPasses) { - // Lower ObjC ARC intrinsics (e.g. `@llvm.objc.clang.arc.use(...)`). - // While Kotlin/Native codegen itself doesn't produce these intrinsics, they might come - // from cinterop "glue" bitcode. - // TODO: Consider adding other ObjC passes. - LLVMAddObjCARCContractPass(modulePasses) - } - - when (config.sanitizer) { - null -> {} - SanitizerKind.ADDRESS -> TODO("Address sanitizer is not supported yet") - SanitizerKind.THREAD -> { - getFunctions(llvmModule) - .filter { LLVMIsDeclaration(it) == 0 } - .forEach { addLlvmFunctionEnumAttribute(it, LlvmFunctionAttribute.SanitizeThread) } - LLVMAddThreadSanitizerPass(modulePasses) + fun execute(llvmModule: LLVMModuleRef) { + val passManager = LLVMCreatePassManager()!! + val passBuilder = LLVMPassManagerBuilderCreate()!! + try { + initLLVMOnce() + LLVMPassManagerBuilderSetOptLevel(passBuilder, config.optimizationLevel.value) + LLVMPassManagerBuilderSetSizeLevel(passBuilder, config.sizeLevel.value) + config.inlineThreshold?.let { threshold -> + LLVMPassManagerBuilderUseInlinerWithThreshold(passBuilder, threshold) } - } - } + LLVMKotlinAddTargetLibraryInfoWrapperPass(passManager, config.targetTriple) + // TargetTransformInfo pass. + LLVMAddAnalysisPasses(targetMachine, passManager) - fun run() { - init() - populatePasses() - logger?.log { - """ - Running LLVM optimizations with the following parameters: - target_triple: ${config.targetTriple} - cpu_model: ${config.cpuModel} - cpu_features: ${config.cpuFeatures} - optimization_level: ${config.optimizationLevel.value} - size_level: ${config.sizeLevel.value} - inline_threshold: ${config.inlineThreshold ?: "default"} - """.trimIndent() + configurePipeline(config, passManager, passBuilder) + executeCustomPreprocessing(config, llvmModule) + // TODO: how to log content of pass manager? + logger?.log { + """ + Running ${pipelineName} with the following parameters: + target_triple: ${config.targetTriple} + cpu_model: ${config.cpuModel} + cpu_features: ${config.cpuFeatures} + optimization_level: ${config.optimizationLevel.value} + size_level: ${config.sizeLevel.value} + inline_threshold: ${config.inlineThreshold ?: "default"} + """.trimIndent() + } + LLVMRunPassManager(passManager, llvmModule) + } finally { + LLVMPassManagerBuilderDispose(passBuilder) + LLVMDisposePassManager(passManager) } - LLVMRunPassManager(modulePasses, llvmModule) } override fun close() { - LLVMPassManagerBuilderDispose(passBuilder) - LLVMDisposeTargetMachine(targetMachine) - LLVMDisposePassManager(modulePasses) + if (targetMachineDelegate.isInitialized()) { + LLVMDisposeTargetMachine(targetMachine) + } arena.clear() } companion object { - @Volatile - @JvmStatic private var isInitialized: Boolean = false + private fun initLLVMTargets() { + memScoped { + LLVMKotlinInitializeTargets() + } + } + + private fun initializeLlvmGlobalPassRegistry() { + val passRegistry = LLVMGetGlobalPassRegistry() + + LLVMInitializeCore(passRegistry) + LLVMInitializeTransformUtils(passRegistry) + LLVMInitializeScalarOpts(passRegistry) + LLVMInitializeVectorization(passRegistry) + LLVMInitializeInstCombine(passRegistry) + LLVMInitializeIPO(passRegistry) + LLVMInitializeInstrumentation(passRegistry) + LLVMInitializeAnalysis(passRegistry) + LLVMInitializeIPA(passRegistry) + LLVMInitializeCodeGen(passRegistry) + LLVMInitializeTarget(passRegistry) + LLVMInitializeObjCARCOpts(passRegistry) + } + @Synchronized - @JvmStatic - fun initLLVMTargets() { + fun initLLVMOnce() { if (!isInitialized) { - memScoped { - LLVMKotlinInitializeTargets() - } + initLLVMTargets() + initializeLlvmGlobalPassRegistry() isInitialized = true } } } } +class MandatoryOptimizationPipeline(config: LlvmPipelineConfig, logger: LoggingContext? = null) : + LlvmOptimizationPipeline(config, logger) { + + override val pipelineName = "Mandatory llvm optimizations" + + override fun configurePipeline(config: LlvmPipelineConfig, manager: LLVMPassManagerRef, builder: LLVMPassManagerBuilderRef) { + if (config.objCPasses) { + // Lower ObjC ARC intrinsics (e.g. `@llvm.objc.clang.arc.use(...)`). + // While Kotlin/Native codegen itself doesn't produce these intrinsics, they might come + // from cinterop "glue" bitcode. + // TODO: Consider adding other ObjC passes. + LLVMAddObjCARCContractPass(manager) + } + } + + override fun executeCustomPreprocessing(config: LlvmPipelineConfig, module: LLVMModuleRef) { + if (config.makeDeclarationsHidden) { + makeVisibilityHiddenLikeLlvmInternalizePass(module) + } + } + + override fun close() { + } +} + +class ModuleOptimizationPipeline(config: LlvmPipelineConfig, logger: LoggingContext? = null) : + LlvmOptimizationPipeline(config, logger) { + override fun configurePipeline(config: LlvmPipelineConfig, manager: LLVMPassManagerRef, builder: LLVMPassManagerBuilderRef) { + LLVMPassManagerBuilderPopulateModulePassManager(builder, manager) + LLVMPassManagerBuilderPopulateFunctionPassManager(builder, manager) + } + + override val pipelineName = "Module LLVM optimizations" +} + +class LTOOptimizationPipeline(config: LlvmPipelineConfig, logger: LoggingContext? = null) : + LlvmOptimizationPipeline(config, logger) { + override fun configurePipeline(config: LlvmPipelineConfig, manager: LLVMPassManagerRef, builder: LLVMPassManagerBuilderRef) { + if (config.internalize) { + LLVMAddInternalizePass(manager, 0) + } + + if (config.globalDce) { + LLVMAddGlobalDCEPass(manager) + } + + // Pipeline that is similar to `llvm-lto`. + LLVMPassManagerBuilderPopulateLTOPassManager(builder, manager, Internalize = 0, RunInliner = 1) + } + + override val pipelineName = "LTO LLVM optimizations" +} + +class ThreadSanitizerPipeline(config: LlvmPipelineConfig, logger: LoggingContext? = null) : + LlvmOptimizationPipeline(config, logger) { + override fun configurePipeline(config: LlvmPipelineConfig, manager: LLVMPassManagerRef, builder: LLVMPassManagerBuilderRef) { + LLVMAddThreadSanitizerPass(manager) + } + + override fun executeCustomPreprocessing(config: LlvmPipelineConfig, module: LLVMModuleRef) { + getFunctions(module) + .filter { LLVMIsDeclaration(it) == 0 } + .forEach { addLlvmFunctionEnumAttribute(it, LlvmFunctionAttribute.SanitizeThread) } + } + + override val pipelineName = "Thread sanitizer instrumentation" +} + + internal fun RelocationModeFlags.currentRelocationMode(context: PhaseContext): RelocationModeFlags.Mode = when (determineLinkerOutput(context)) { LinkerOutputKind.DYNAMIC_LIBRARY -> dynamicLibraryRelocationMode @@ -314,4 +352,4 @@ private fun RelocationModeFlags.Mode.translateToLlvmRelocMode() = when (this) { RelocationModeFlags.Mode.PIC -> LLVMRelocMode.LLVMRelocPIC RelocationModeFlags.Mode.STATIC -> LLVMRelocMode.LLVMRelocStatic RelocationModeFlags.Mode.DEFAULT -> LLVMRelocMode.LLVMRelocDefault -} \ No newline at end of file +} diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/RuntimeLinkageStrategy.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/RuntimeLinkageStrategy.kt index 343d994d515..6144bb046d3 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/RuntimeLinkageStrategy.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/RuntimeLinkageStrategy.kt @@ -52,9 +52,15 @@ internal sealed class RuntimeLinkageStrategy { } } val config = createLTOPipelineConfigForRuntime(generationState) - LlvmOptimizationPipeline(config, runtimeModule, generationState).use { - it.run() + + // TODO: reconsider pipeline here. Module optimizations instead of LTO can make a lot of sense, but require testing + MandatoryOptimizationPipeline(config, generationState).use { + it.execute(runtimeModule) } + LTOOptimizationPipeline(config, generationState).use { + it.execute(runtimeModule) + } + return listOf(runtimeModule) } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Bitcode.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Bitcode.kt index 69c980277b4..caafabb6092 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Bitcode.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Bitcode.kt @@ -8,11 +8,12 @@ package org.jetbrains.kotlin.backend.konan.driver.phases import llvm.LLVMDumpModule import llvm.LLVMModuleRef import llvm.LLVMWriteBitcodeToFile -import org.jetbrains.kotlin.backend.common.phaser.ActionState +import org.jetbrains.kotlin.backend.common.LoggingContext import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.NativeGenerationState import org.jetbrains.kotlin.backend.konan.checkLlvmModuleExternalCalls import org.jetbrains.kotlin.backend.konan.createLTOFinalPipelineConfig +import org.jetbrains.kotlin.backend.konan.driver.BasicPhaseContext import org.jetbrains.kotlin.backend.konan.driver.PhaseContext import org.jetbrains.kotlin.backend.konan.driver.PhaseEngine import org.jetbrains.kotlin.backend.konan.driver.utilities.LlvmIrHolder @@ -22,6 +23,7 @@ import org.jetbrains.kotlin.backend.konan.llvm.coverage.runCoveragePass import org.jetbrains.kotlin.backend.konan.llvm.verifyModule import org.jetbrains.kotlin.backend.konan.optimizations.RemoveRedundantSafepointsPass import org.jetbrains.kotlin.backend.konan.optimizations.removeMultipleThreadDataLoads +import org.jetbrains.kotlin.konan.target.SanitizerKind import java.io.File @@ -58,16 +60,47 @@ internal val RewriteExternalCallsCheckerGlobals = createSimpleNamedCompilerPhase addFunctionsListSymbolForChecker(context) } -internal val BitcodeOptimizationPhase = createSimpleNamedCompilerPhase( - name = "BitcodeOptimization", +internal class OptimizationState( + konanConfig: KonanConfig, + val module: LLVMModuleRef, + val llvmConfig: LlvmPipelineConfig +) : BasicPhaseContext(konanConfig) + +internal fun optimizationPipelinePass(name: String, description: String, pipeline: (LlvmPipelineConfig, LoggingContext) -> LlvmOptimizationPipeline) = + createSimpleNamedCompilerPhase( + name = name, + description = description, + postactions = getDefaultLlvmModuleActions(), + ) { context, _ -> + pipeline(context.llvmConfig, context).use { + it.execute(context.module) + } + } + + +internal val MandatoryBitcodeLLVMPostprocessingPhase = optimizationPipelinePass( + name = "MandatoryBitcodeLLVMPostprocessingPhase", + description = "Mandatory bitcode llvm postprocessing", + pipeline = ::MandatoryOptimizationPipeline, +) + +internal val ModuleBitcodeOptimizationPhase = optimizationPipelinePass( + name = "ModuleBitcodeOptimization", description = "Optimize bitcode", - postactions = getDefaultLlvmModuleActions(), -) { context, _ -> - val config = createLTOFinalPipelineConfig(context, context.llvm.targetTriple, closedWorld = context.llvmModuleSpecification.isFinal) - LlvmOptimizationPipeline(config, context.llvm.module, context).use { - it.run() - } -} + pipeline = ::ModuleOptimizationPipeline, +) + +internal val LTOBitcodeOptimizationPhase = optimizationPipelinePass( + name = "LTOBitcodeOptimization", + description = "Runs llvm lto pipeline", + pipeline = ::LTOOptimizationPipeline +) + +internal val ThreadSanitizerPhase = optimizationPipelinePass( + name = "ThreadSanitizer", + description = "Prepare to run with thread sanitizer", + pipeline = ::ThreadSanitizerPipeline, +) internal val CoveragePhase = createSimpleNamedCompilerPhase( name = "Coverage", @@ -126,7 +159,17 @@ internal fun PhaseEngine.runBitcodePostProcessing() { if (checkExternalCalls) { runPhase(CheckExternalCallsPhase) } - runPhase(BitcodeOptimizationPhase) + val optimizationConfig = createLTOFinalPipelineConfig(context, context.llvm.targetTriple, closedWorld = context.llvmModuleSpecification.isFinal) + useContext(OptimizationState(context.config, context.llvmModule, optimizationConfig)) { + it.runPhase(MandatoryBitcodeLLVMPostprocessingPhase) + it.runPhase(ModuleBitcodeOptimizationPhase) + it.runPhase(LTOBitcodeOptimizationPhase) + when (context.config.sanitizer) { + SanitizerKind.THREAD -> it.runPhase(ThreadSanitizerPhase) + SanitizerKind.ADDRESS -> context.reportCompilationError("Address sanitizer is not supported yet") + null -> {} + } + } runPhase(CoveragePhase) if (context.config.memoryModel == MemoryModel.EXPERIMENTAL) { runPhase(RemoveRedundantSafepointsPhase)