[Kotlin/Native] Avoid jni overhead when optimizing bitcode.
The phase to eliminate redundant function prologue safepoints was written in Kotlin which meant that it was performing a lot of jni calls. Moving the simple optimization to C code speeds things up dramatically. On a large project this reduces the time for this phase from 11.8 seconds to 1.4 seconds.
This commit is contained in:
+1
-1
@@ -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()
|
||||
)
|
||||
|
||||
+2
-53
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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 \
|
||||
|
||||
@@ -57,4 +57,4 @@ int LLVMInlineCall(LLVMValueRef call) {
|
||||
|
||||
void LLVMAddThreadSanitizerPass(LLVMPassManagerRef PM) {
|
||||
unwrap(PM)->add(createThreadSanitizerLegacyPassPass());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <CAPIExtensions.h>
|
||||
#include <RemoveRedundantSafepoints.h>
|
||||
#include <llvm/IR/Constants.h>
|
||||
#include <llvm/IR/Instructions.h>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
static constexpr const char* functionPrologueSafepointName = "Kotlin_mm_safePointFunctionPrologue";
|
||||
|
||||
static constexpr size_t functionPrologueSafepointNameLength =
|
||||
std::char_traits<char>::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<LLVMValueRef> 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);
|
||||
}
|
||||
}
|
||||
@@ -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 <llvm-c/Core.h>
|
||||
#include <llvm-c/Target.h>
|
||||
|
||||
# ifdef __cplusplus
|
||||
extern "C" {
|
||||
# endif
|
||||
|
||||
void LLVMKotlinRemoveRedundantSafepoints(LLVMModuleRef module, int isSafePointInliningAllowed);
|
||||
|
||||
# ifdef __cplusplus
|
||||
}
|
||||
# endif
|
||||
Reference in New Issue
Block a user