[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",
|
name = "RemoveRedundantSafepoints",
|
||||||
description = "Remove function prologue safepoints inlined to another function",
|
description = "Remove function prologue safepoints inlined to another function",
|
||||||
op = { context, _ ->
|
op = { context, _ ->
|
||||||
RemoveRedundantSafepointsPass(context).runOnModule(
|
RemoveRedundantSafepointsPass().runOnModule(
|
||||||
module = context.generationState.llvm.module,
|
module = context.generationState.llvm.module,
|
||||||
isSafepointInliningAllowed = context.shouldInlineSafepoints()
|
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, 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)
|
* Also, calls, which are not removed are inlined (except arm32 apple targets)
|
||||||
*/
|
*/
|
||||||
internal class RemoveRedundantSafepointsPass(
|
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
|
|
||||||
|
|
||||||
fun runOnModule(module: LLVMModuleRef, isSafepointInliningAllowed: Boolean) {
|
fun runOnModule(module: LLVMModuleRef, isSafepointInliningAllowed: Boolean) {
|
||||||
totalPrologueSafepointsCount = 0
|
LLVMKotlinRemoveRedundantSafepoints(module, if (isSafepointInliningAllowed) 1 else 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"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
headers = llvm-c/Core.h llvm-c/Target.h llvm-c/Analysis.h llvm-c/BitWriter.h \
|
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/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/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 \
|
compilerOpts = -std=c99 \
|
||||||
-Wall -W -Wno-unused-parameter -Wwrite-strings -Wmissing-field-initializers \
|
-Wall -W -Wno-unused-parameter -Wwrite-strings -Wmissing-field-initializers \
|
||||||
|
|||||||
@@ -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