[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:
Mads Ager
2022-12-15 12:45:26 +01:00
committed by Space Team
parent 53b98503ed
commit d87ed38cbf
6 changed files with 112 additions and 57 deletions
@@ -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()
)
@@ -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)
}
}
+2 -2
View File
@@ -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