[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:
+3
@@ -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)
|
||||
|
||||
+12
@@ -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",
|
||||
|
||||
+63
@@ -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"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user