diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index 84aff74f8f3..82631c3f88a 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -545,6 +545,7 @@ private val bitcodePostprocessingPhase = NamedCompilerPhase( lower = checkExternalCallsPhase then bitcodeOptimizationPhase then coveragePhase then + removeRedundantSafepointsPhase then optimizeTLSDataLoadsPhase then rewriteExternalCallsCheckerGlobals ) @@ -644,6 +645,8 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) { disableUnless(fileInitializersPhase, getBoolean(KonanConfigKeys.PROPERTY_LAZY_INITIALIZATION)) disableUnless(removeRedundantCallsToFileInitializersPhase, getBoolean(KonanConfigKeys.PROPERTY_LAZY_INITIALIZATION)) + disableUnless(removeRedundantSafepointsPhase, config.configuration.get(BinaryOptions.memoryModel) == MemoryModel.EXPERIMENTAL) + val isDescriptorsOnlyLibrary = config.metadataKlib == true disableIf(psiToIrPhase, isDescriptorsOnlyLibrary) disableIf(destroySymbolTablePhase, isDescriptorsOnlyLibrary) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt index c1581983041..e10fd7a129e 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.backend.konan.llvm import llvm.* +import org.jetbrains.kotlin.backend.common.LoggingContext import org.jetbrains.kotlin.backend.common.phaser.CompilerPhase import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig import org.jetbrains.kotlin.backend.common.phaser.PhaserState @@ -25,6 +26,7 @@ import org.jetbrains.kotlin.ir.util.isFunction import org.jetbrains.kotlin.ir.util.isReal import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.ir.visitors.* +import org.jetbrains.kotlin.konan.target.Architecture import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.addToStdlib.cast @@ -386,6 +388,16 @@ internal val produceOutputPhase = namedUnitPhase( } ) +internal val removeRedundantSafepointsPhase = makeKonanModuleOpPhase( + name = "RemoveRedundantSafepoints", + description = "Leave only one safepoint in a basic block", + op = { context, _ -> + if (context.config.target.architecture == Architecture.ARM32 && context.config.target.family.isAppleFamily) { + RemoveRedundantSafepointsPass(context as LoggingContext).runOnModule(context.llvmModule!!) + } + } +) + internal val verifyBitcodePhase = makeKonanModuleOpPhase( name = "VerifyBitcode", description = "Verify bitcode", diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/RemoveRedundantSafepointsPass.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/RemoveRedundantSafepointsPass.kt new file mode 100644 index 00000000000..ae24c2663e9 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/RemoveRedundantSafepointsPass.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.konan.optimizations + +import llvm.* +import org.jetbrains.kotlin.backend.common.LoggingContext +import org.jetbrains.kotlin.backend.konan.llvm.getBasicBlocks +import org.jetbrains.kotlin.backend.konan.llvm.getFunctions +import org.jetbrains.kotlin.backend.konan.llvm.getInstructions +import org.jetbrains.kotlin.backend.konan.llvm.name + +/** + * Removes all Kotlin_mm_safePointFunctionPrologue from basic block except the first one. + * + * Currently, this pass is useful only for watchos_arm32, ios_arm32 targets because Kotlin_mm_* functions are marked + * as noinline there. + */ +class RemoveRedundantSafepointsPass( + private val loggingContext: LoggingContext +) { + var totalPrologueSafepointsCount = 0 + var removedPrologueSafepointsCount = 0 + + fun runOnFunction(function: LLVMValueRef) { + getBasicBlocks(function).forEach { bb -> + val unnecessaryPrologueSafepointCallsites = getInstructions(bb) + .filter { isPrologueSafepointCallsite(it) } + .onEach { totalPrologueSafepointsCount += 1 } + .drop(1) + .toList() + unnecessaryPrologueSafepointCallsites.forEach { + LLVMInstructionEraseFromParent(it) + removedPrologueSafepointsCount += 1 + } + } + } + + private fun isPrologueSafepointCallsite(insn: LLVMValueRef): Boolean = + (LLVMIsACallInst(insn) != null || LLVMIsAInvokeInst(insn) != null) + && LLVMGetCalledValue(insn)?.name == prologueSafepointFunctionName + + fun runOnModule(module: LLVMModuleRef) { + totalPrologueSafepointsCount = 0 + removedPrologueSafepointsCount = 0 + getFunctions(module) + .filter { it.name?.startsWith("kfun:") == true } + .filterNot { LLVMIsDeclaration(it) == 1 } + .forEach(this::runOnFunction) + loggingContext.log { + """ + Total prologue safepoints: $totalPrologueSafepointsCount + Removed prologue safepoints: $removedPrologueSafepointsCount + """.trimIndent() + } + } + + companion object { + private const val prologueSafepointFunctionName = "Kotlin_mm_safePointFunctionPrologue" + } +} \ No newline at end of file