From f4ea929d8ec94cb3b0f94589f098daeafc6b86b0 Mon Sep 17 00:00:00 2001 From: Pavel Kunyavskiy Date: Fri, 3 Jun 2022 18:45:35 +0200 Subject: [PATCH] [K/N] Better removing of extra safepoints --- .../jetbrains/kotlin/backend/konan/Context.kt | 9 +++- .../backend/konan/llvm/BitcodePhases.kt | 9 ++-- .../RemoveRedundantSafepointsPass.kt | 49 ++++++++++++------- .../src/main/cpp/CAPIExtensions.cpp | 6 +++ .../src/main/include/CAPIExtensions.h | 2 + kotlin-native/runtime/src/mm/cpp/Memory.cpp | 3 +- 6 files changed, 53 insertions(+), 25 deletions(-) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 27805ed42f5..71db05ace19 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.backend.konan import llvm.* +import org.jetbrains.kotlin.backend.common.LoggingContext import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.backend.konan.ir.KonanIr import org.jetbrains.kotlin.library.SerializedMetadata @@ -457,6 +458,10 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { fun shouldUseDebugInfoFromNativeLibs() = shouldContainAnyDebugInfo() && config.useDebugInfoInNativeLibs fun shouldOptimize() = config.optimizationsEnabled + fun shouldInlineSafepoints() = when { + config.target.family.isAppleFamily -> config.target.architecture != Architecture.ARM32 // disable for watchos_arm32 and similar + else -> true + } fun ghaEnabled() = ::globalHierarchyAnalysisResult.isInitialized fun useLazyFileInitializers() = config.propertyLazyInitialization @@ -530,11 +535,11 @@ private fun MemberScope.getContributedClassifier(name: String) = private fun MemberScope.getContributedFunctions(name: String) = this.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BUILTINS) -internal class ContextLogger(val context: Context) { +internal class ContextLogger(val context: LoggingContext) { operator fun String.unaryPlus() = context.log { this } } -internal fun Context.logMultiple(messageBuilder: ContextLogger.() -> Unit) { +internal fun LoggingContext.logMultiple(messageBuilder: ContextLogger.() -> Unit) { if (!inVerbosePhase) return with(ContextLogger(this)) { messageBuilder() } } \ No newline at end of file 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 e10fd7a129e..b3379c7bf7b 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 @@ -390,11 +390,12 @@ internal val produceOutputPhase = namedUnitPhase( internal val removeRedundantSafepointsPhase = makeKonanModuleOpPhase( name = "RemoveRedundantSafepoints", - description = "Leave only one safepoint in a basic block", + description = "Remove function prologue safepoints inlined to another function", op = { context, _ -> - if (context.config.target.architecture == Architecture.ARM32 && context.config.target.family.isAppleFamily) { - RemoveRedundantSafepointsPass(context as LoggingContext).runOnModule(context.llvmModule!!) - } + RemoveRedundantSafepointsPass(context).runOnModule( + module = context.llvmModule!!, + isSafepointInliningAllowed = context.shouldInlineSafepoints() + ) } ) 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 index ae24c2663e9..7b4ab8d7a41 100644 --- 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 @@ -7,34 +7,50 @@ package org.jetbrains.kotlin.backend.konan.optimizations import llvm.* import org.jetbrains.kotlin.backend.common.LoggingContext +import org.jetbrains.kotlin.backend.konan.llvm.* 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 +import org.jetbrains.kotlin.backend.konan.logMultiple /** * 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. + * Also, if first basic block in function contains call to Kotlin_mm_safePointFunctionPrologue, all other calls would be removed. + * Also, calls, which are not removed are inlined (except arm32 apple targets) */ -class RemoveRedundantSafepointsPass( +internal class RemoveRedundantSafepointsPass( private val loggingContext: LoggingContext ) { var totalPrologueSafepointsCount = 0 var removedPrologueSafepointsCount = 0 - fun runOnFunction(function: LLVMValueRef) { + fun runOnFunction(function: LLVMValueRef, isSafepointInliningAllowed: Boolean) { + val firstBlock = LLVMGetFirstBasicBlock(function) ?: return + val firstBlockHasSafepoint = getInstructions(firstBlock).any { isPrologueSafepointCallsite(it) } getBasicBlocks(function).forEach { bb -> - val unnecessaryPrologueSafepointCallsites = getInstructions(bb) + val removeFirst = firstBlockHasSafepoint && bb != firstBlock + val prologueSafepointCallsites = getInstructions(bb) .filter { isPrologueSafepointCallsite(it) } - .onEach { totalPrologueSafepointsCount += 1 } - .drop(1) .toList() - unnecessaryPrologueSafepointCallsites.forEach { + totalPrologueSafepointsCount += prologueSafepointCallsites.size + prologueSafepointCallsites.drop(if (removeFirst) 0 else 1).forEach { LLVMInstructionEraseFromParent(it) removedPrologueSafepointsCount += 1 } + if (!removeFirst && isSafepointInliningAllowed) { + prologueSafepointCallsites + .firstOrNull() + ?.apply { + if (LLVMIsDeclaration(LLVMGetCalledValue(this)) == 0) { + if (LLVMInlineCall(this) == 0) { + loggingContext.logMultiple { + +"Failed to Inline safepoint to ${function.name}" + +llvm2string(function) + } + } + } + } + } } } @@ -42,18 +58,15 @@ class RemoveRedundantSafepointsPass( (LLVMIsACallInst(insn) != null || LLVMIsAInvokeInst(insn) != null) && LLVMGetCalledValue(insn)?.name == prologueSafepointFunctionName - fun runOnModule(module: LLVMModuleRef) { + fun runOnModule(module: LLVMModuleRef, isSafepointInliningAllowed: Boolean) { 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() + .forEach { runOnFunction(it, isSafepointInliningAllowed) } + loggingContext.logMultiple { + +"Total prologue safepoints: $totalPrologueSafepointsCount" + +"Removed prologue safepoints: $removedPrologueSafepointsCount" } } diff --git a/kotlin-native/libllvmext/src/main/cpp/CAPIExtensions.cpp b/kotlin-native/libllvmext/src/main/cpp/CAPIExtensions.cpp index 2f41a7ed387..92ac4bed4e5 100644 --- a/kotlin-native/libllvmext/src/main/cpp/CAPIExtensions.cpp +++ b/kotlin-native/libllvmext/src/main/cpp/CAPIExtensions.cpp @@ -9,6 +9,7 @@ #include #include #include +#include using namespace llvm; @@ -47,3 +48,8 @@ void LLVMKotlinInitializeTargets() { void LLVMSetNoTailCall(LLVMValueRef Call) { unwrap(Call)->setTailCallKind(CallInst::TCK_NoTail); } + +int LLVMInlineCall(LLVMValueRef call) { + InlineFunctionInfo IFI; + return InlineFunction(*unwrap(call), IFI).isSuccess(); +} \ No newline at end of file diff --git a/kotlin-native/libllvmext/src/main/include/CAPIExtensions.h b/kotlin-native/libllvmext/src/main/include/CAPIExtensions.h index b618ec4632d..d939d35f0ae 100644 --- a/kotlin-native/libllvmext/src/main/include/CAPIExtensions.h +++ b/kotlin-native/libllvmext/src/main/include/CAPIExtensions.h @@ -23,6 +23,8 @@ void LLVMKotlinInitializeTargets(); void LLVMSetNoTailCall(LLVMValueRef Call); +int LLVMInlineCall(LLVMValueRef call); + # ifdef __cplusplus } # endif \ No newline at end of file diff --git a/kotlin-native/runtime/src/mm/cpp/Memory.cpp b/kotlin-native/runtime/src/mm/cpp/Memory.cpp index aabf8ddb557..aae4336d1c5 100644 --- a/kotlin-native/runtime/src/mm/cpp/Memory.cpp +++ b/kotlin-native/runtime/src/mm/cpp/Memory.cpp @@ -586,7 +586,8 @@ extern "C" void CheckGlobalsAccessible() { // Always accessible } -extern "C" RUNTIME_NOTHROW CODEGEN_INLINE_POLICY void Kotlin_mm_safePointFunctionPrologue() { +// it would be inlined manually in RemoveRedundantSafepointsPass +extern "C" RUNTIME_NOTHROW NO_INLINE void Kotlin_mm_safePointFunctionPrologue() { auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData(); AssertThreadState(threadData, ThreadState::kRunnable); threadData->gc().SafePointFunctionPrologue();