[K/N] Introduce RemoveRedundantSafepointsPass

Since we do not inline Kotlin_mm_* functions on watchos_arm32 and
ios_arm32 targets, there are a lot of unnecessary subsequent safepoints.
To reduce binary size and avoid unnecessary safepoints we apply a simple
optimization: remove all calls to Kotlin_mm_safePointFunctionPrologue in
a basic block except the first one.

^KT-51737 fixed.
This commit is contained in:
Sergey Bogolepov
2022-03-24 16:06:31 +03:00
committed by Space
parent bd8f49c5d6
commit f90a56c131
3 changed files with 78 additions and 0 deletions
@@ -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)
@@ -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",
@@ -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"
}
}