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 6477c0676bf..61132540280 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 @@ -324,7 +324,7 @@ internal val removeRedundantSafepointsPhase = makeKonanModuleOpPhase( name = "RemoveRedundantSafepoints", description = "Remove function prologue safepoints inlined to another function", op = { context, _ -> - RemoveRedundantSafepointsPass(context).runOnModule( + RemoveRedundantSafepointsPass().runOnModule( module = context.generationState.llvm.module, 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 7b4ab8d7a41..9df1e167bf9 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 @@ -18,59 +18,8 @@ import org.jetbrains.kotlin.backend.konan.logMultiple * 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) */ -internal class RemoveRedundantSafepointsPass( - private val loggingContext: LoggingContext -) { - var totalPrologueSafepointsCount = 0 - var removedPrologueSafepointsCount = 0 - - fun runOnFunction(function: LLVMValueRef, isSafepointInliningAllowed: Boolean) { - val firstBlock = LLVMGetFirstBasicBlock(function) ?: return - val firstBlockHasSafepoint = getInstructions(firstBlock).any { isPrologueSafepointCallsite(it) } - getBasicBlocks(function).forEach { bb -> - val removeFirst = firstBlockHasSafepoint && bb != firstBlock - val prologueSafepointCallsites = getInstructions(bb) - .filter { isPrologueSafepointCallsite(it) } - .toList() - 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) - } - } - } - } - } - } - } - - private fun isPrologueSafepointCallsite(insn: LLVMValueRef): Boolean = - (LLVMIsACallInst(insn) != null || LLVMIsAInvokeInst(insn) != null) - && LLVMGetCalledValue(insn)?.name == prologueSafepointFunctionName - +internal class RemoveRedundantSafepointsPass { fun runOnModule(module: LLVMModuleRef, isSafepointInliningAllowed: Boolean) { - totalPrologueSafepointsCount = 0 - removedPrologueSafepointsCount = 0 - getFunctions(module) - .filterNot { LLVMIsDeclaration(it) == 1 } - .forEach { runOnFunction(it, isSafepointInliningAllowed) } - loggingContext.logMultiple { - +"Total prologue safepoints: $totalPrologueSafepointsCount" - +"Removed prologue safepoints: $removedPrologueSafepointsCount" - } - } - - companion object { - private const val prologueSafepointFunctionName = "Kotlin_mm_safePointFunctionPrologue" + LLVMKotlinRemoveRedundantSafepoints(module, if (isSafepointInliningAllowed) 1 else 0) } } \ No newline at end of file diff --git a/kotlin-native/backend.native/llvm.def b/kotlin-native/backend.native/llvm.def index 4ae8ab8607a..e10cc2734b6 100644 --- a/kotlin-native/backend.native/llvm.def +++ b/kotlin-native/backend.native/llvm.def @@ -1,9 +1,9 @@ headers = llvm-c/Core.h llvm-c/Target.h llvm-c/Analysis.h llvm-c/BitWriter.h \ llvm-c/BitReader.h llvm-c/Transforms/PassManagerBuilder.h llvm-c/Transforms/IPO.h \ llvm-c/TargetMachine.h llvm-c/Target.h llvm-c/Linker.h llvm-c/Initialization.h \ - llvm-c/DebugInfo.h DebugInfoC.h CoverageMappingC.h CAPIExtensions.h + llvm-c/DebugInfo.h DebugInfoC.h CoverageMappingC.h CAPIExtensions.h RemoveRedundantSafepoints.h -headerFilter = llvm-c/* llvm-c/**/* DebugInfoC.h CoverageMappingC.h CAPIExtensions.h +headerFilter = llvm-c/* llvm-c/**/* DebugInfoC.h CoverageMappingC.h CAPIExtensions.h RemoveRedundantSafepoints.h compilerOpts = -std=c99 \ -Wall -W -Wno-unused-parameter -Wwrite-strings -Wmissing-field-initializers \ diff --git a/kotlin-native/libllvmext/src/main/cpp/CAPIExtensions.cpp b/kotlin-native/libllvmext/src/main/cpp/CAPIExtensions.cpp index 3eaad2291e9..0a7e3e1e4ba 100644 --- a/kotlin-native/libllvmext/src/main/cpp/CAPIExtensions.cpp +++ b/kotlin-native/libllvmext/src/main/cpp/CAPIExtensions.cpp @@ -57,4 +57,4 @@ int LLVMInlineCall(LLVMValueRef call) { void LLVMAddThreadSanitizerPass(LLVMPassManagerRef PM) { unwrap(PM)->add(createThreadSanitizerLegacyPassPass()); -} \ No newline at end of file +} diff --git a/kotlin-native/libllvmext/src/main/cpp/RemoveRedundantSafepoints.cpp b/kotlin-native/libllvmext/src/main/cpp/RemoveRedundantSafepoints.cpp new file mode 100644 index 00000000000..954e1786608 --- /dev/null +++ b/kotlin-native/libllvmext/src/main/cpp/RemoveRedundantSafepoints.cpp @@ -0,0 +1,84 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +#include +#include +#include +#include + +using namespace llvm; + +static constexpr const char* functionPrologueSafepointName = "Kotlin_mm_safePointFunctionPrologue"; + +static constexpr size_t functionPrologueSafepointNameLength = + std::char_traits::length(functionPrologueSafepointName); + +static bool InstructionIsPrologueSafepoint(LLVMValueRef instruction) { + if (LLVMIsACallInst(instruction) || LLVMIsAInvokeInst(instruction)) { + size_t calledNameLength = 0; + const char* calledName = LLVMGetValueName2(LLVMGetCalledValue(instruction), &calledNameLength); + return functionPrologueSafepointNameLength == calledNameLength && + strncmp(calledName, functionPrologueSafepointName, calledNameLength) == 0; + } + return false; +} + +static bool BlockHasSafepointInstruction(LLVMBasicBlockRef block) { + LLVMValueRef current = LLVMGetFirstInstruction(block); + while (current) { + if (InstructionIsPrologueSafepoint(current)) { + return true; + } + current = LLVMGetNextInstruction(current); + } + return false; +} + +static void RemoveOrInlinePrologueSafepointInstructions( + LLVMBasicBlockRef block, bool removeFirst, bool isSafepointInliningAllowed) { + // Collect safepoint calls to erase and inline. + LLVMValueRef toInline = nullptr; + std::vector toErase; + bool first = true; + LLVMValueRef current = LLVMGetFirstInstruction(block); + while (current) { + if (InstructionIsPrologueSafepoint(current)) { + if (!first || removeFirst) { + toErase.push_back(current); + } else if (isSafepointInliningAllowed && !LLVMIsDeclaration(LLVMGetCalledValue(current))) { + toInline = current; + } + first = false; + } + current = LLVMGetNextInstruction(current); + } + // Perform actual modifications after iteration. + for (auto it = toErase.cbegin(); it != toErase.cend(); ++it) { + LLVMInstructionEraseFromParent(*it); + } + if (toInline) { + LLVMInlineCall(toInline); + } +} + +void LLVMKotlinRemoveRedundantSafepoints(LLVMModuleRef module, int isSafepointInliningAllowed) { + bool inliningAllowed = isSafepointInliningAllowed != 0; + LLVMValueRef currentFunction = LLVMGetFirstFunction(module); + while (currentFunction) { + if (!LLVMIsDeclaration(currentFunction)) { + LLVMBasicBlockRef firstBlock = LLVMGetFirstBasicBlock(currentFunction); + if (firstBlock) { + bool firstBlockHasSafepoint = BlockHasSafepointInstruction(firstBlock); + RemoveOrInlinePrologueSafepointInstructions(firstBlock, false, inliningAllowed); + LLVMBasicBlockRef currentBlock = LLVMGetNextBasicBlock(firstBlock); + while (currentBlock) { + RemoveOrInlinePrologueSafepointInstructions(currentBlock, firstBlockHasSafepoint, inliningAllowed); + currentBlock = LLVMGetNextBasicBlock(currentBlock); + } + } + } + currentFunction = LLVMGetNextFunction(currentFunction); + } +} diff --git a/kotlin-native/libllvmext/src/main/include/RemoveRedundantSafepoints.h b/kotlin-native/libllvmext/src/main/include/RemoveRedundantSafepoints.h new file mode 100644 index 00000000000..c2a7bff5ce5 --- /dev/null +++ b/kotlin-native/libllvmext/src/main/include/RemoveRedundantSafepoints.h @@ -0,0 +1,22 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +#ifndef LIBLLVMEXT_REMOVE_REDUNDANT_SAFEPOINTS_H +#define LIBLLVMEXT_REMOVE_REDUNDANT_SAFEPOINTS_H + +#endif // LIBLLVMEXT_REMOVE_REDUNDANT_SAFEPOINTS_H + +#include +#include + +# ifdef __cplusplus +extern "C" { +# endif + +void LLVMKotlinRemoveRedundantSafepoints(LLVMModuleRef module, int isSafePointInliningAllowed); + +# ifdef __cplusplus +} +# endif \ No newline at end of file