Make static cache symbols hidden
Enables DCE for cached code when linking dynamic library too
This commit is contained in:
committed by
SvyatoslavScherbina
parent
f282ff0f55
commit
990c754f77
+8
@@ -5,6 +5,8 @@ import kotlinx.cinterop.memScoped
|
|||||||
import kotlinx.cinterop.ptr
|
import kotlinx.cinterop.ptr
|
||||||
import kotlinx.cinterop.value
|
import kotlinx.cinterop.value
|
||||||
import llvm.*
|
import llvm.*
|
||||||
|
import org.jetbrains.kotlin.backend.konan.llvm.makeVisibilityHiddenLikeLlvmInternalizePass
|
||||||
|
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||||
import org.jetbrains.kotlin.konan.target.Configurables
|
import org.jetbrains.kotlin.konan.target.Configurables
|
||||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
import org.jetbrains.kotlin.konan.target.ZephyrConfigurables
|
import org.jetbrains.kotlin.konan.target.ZephyrConfigurables
|
||||||
@@ -175,6 +177,12 @@ internal fun runLlvmOptimizationPipeline(context: Context) {
|
|||||||
// Since we are in a "closed world" internalization can be safely used
|
// Since we are in a "closed world" internalization can be safely used
|
||||||
// to reduce size of a bitcode with global dce.
|
// to reduce size of a bitcode with global dce.
|
||||||
LLVMAddInternalizePass(modulePasses, 0)
|
LLVMAddInternalizePass(modulePasses, 0)
|
||||||
|
} else if (context.config.produce == CompilerOutputKind.STATIC_CACHE) {
|
||||||
|
// Hidden visibility makes symbols internal when linking the binary.
|
||||||
|
// When producing dynamic library, this enables stripping unused symbols from binary with -dead_strip flag,
|
||||||
|
// similar to DCE enabled by internalize but later:
|
||||||
|
makeVisibilityHiddenLikeLlvmInternalizePass(llvmModule)
|
||||||
|
// Important for binary size, workarounds references to undefined symbols from interop libraries.
|
||||||
}
|
}
|
||||||
LLVMAddGlobalDCEPass(modulePasses)
|
LLVMAddGlobalDCEPass(modulePasses)
|
||||||
|
|
||||||
|
|||||||
+12
@@ -419,6 +419,18 @@ internal fun node(vararg it:LLVMValueRef) = LLVMMDNodeInContext(llvmContext, it.
|
|||||||
|
|
||||||
internal fun LLVMValueRef.setUnaligned() = apply { LLVMSetAlignment(this, 1) }
|
internal fun LLVMValueRef.setUnaligned() = apply { LLVMSetAlignment(this, 1) }
|
||||||
|
|
||||||
|
internal fun getOperands(value: LLVMValueRef) =
|
||||||
|
(0 until LLVMGetNumOperands(value)).map { LLVMGetOperand(value, it)!! }
|
||||||
|
|
||||||
|
internal fun getGlobalAliases(module: LLVMModuleRef) =
|
||||||
|
generateSequence(LLVMGetFirstGlobalAlias(module), { LLVMGetNextGlobalAlias(it) })
|
||||||
|
|
||||||
|
internal fun getFunctions(module: LLVMModuleRef) =
|
||||||
|
generateSequence(LLVMGetFirstFunction(module), { LLVMGetNextFunction(it) })
|
||||||
|
|
||||||
|
internal fun getGlobals(module: LLVMModuleRef) =
|
||||||
|
generateSequence(LLVMGetFirstGlobal(module), { LLVMGetNextGlobal(it) })
|
||||||
|
|
||||||
fun LLVMTypeRef.isFloatingPoint(): Boolean = when (llvm.LLVMGetTypeKind(this)) {
|
fun LLVMTypeRef.isFloatingPoint(): Boolean = when (llvm.LLVMGetTypeKind(this)) {
|
||||||
LLVMTypeKind.LLVMFloatTypeKind, LLVMTypeKind.LLVMDoubleTypeKind -> true
|
LLVMTypeKind.LLVMFloatTypeKind, LLVMTypeKind.LLVMDoubleTypeKind -> true
|
||||||
else -> false
|
else -> false
|
||||||
|
|||||||
+59
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.backend.konan.llvm
|
||||||
|
|
||||||
|
import llvm.*
|
||||||
|
import org.jetbrains.kotlin.utils.DFS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies hidden visibility to symbols similarly to LLVM's internalize pass:
|
||||||
|
* it makes hidden the symbols that are made internal by internalize.
|
||||||
|
*/
|
||||||
|
fun makeVisibilityHiddenLikeLlvmInternalizePass(module: LLVMModuleRef) {
|
||||||
|
// Note: the implementation below generally follows InternalizePass::internalizeModule,
|
||||||
|
// but omits some details for simplicity.
|
||||||
|
|
||||||
|
// TODO: LLVM handles some additional cases.
|
||||||
|
val alwaysPreserved = getLlvmUsed(module)
|
||||||
|
|
||||||
|
(getFunctions(module) + getGlobals(module) + getGlobalAliases(module))
|
||||||
|
.filter {
|
||||||
|
when (LLVMGetLinkage(it)) {
|
||||||
|
LLVMLinkage.LLVMInternalLinkage, LLVMLinkage.LLVMPrivateLinkage -> false
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.filter { LLVMIsDeclaration(it) == 0 }
|
||||||
|
.minus(alwaysPreserved)
|
||||||
|
.forEach {
|
||||||
|
LLVMSetVisibility(it, LLVMVisibility.LLVMHiddenVisibility)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLlvmUsed(module: LLVMModuleRef): Set<LLVMValueRef> {
|
||||||
|
val llvmUsed = LLVMGetNamedGlobal(module, "llvm.used") ?: return emptySet()
|
||||||
|
val llvmUsedValue = LLVMGetInitializer(llvmUsed) ?: return emptySet()
|
||||||
|
|
||||||
|
// Note: llvm.used value is an array of globals, wrapped into bitcasts, GEPs and other instructions;
|
||||||
|
// see llvm::collectUsedGlobalVariables.
|
||||||
|
// Conservatively extract all involved globals for simplicity:
|
||||||
|
return DFS.dfs(
|
||||||
|
/* nodes = */ listOf(llvmUsedValue),
|
||||||
|
/* neighbors = */ { value -> getOperands(value) },
|
||||||
|
object : DFS.CollectingNodeHandler<LLVMValueRef, LLVMValueRef, MutableSet<LLVMValueRef>>(mutableSetOf()) {
|
||||||
|
override fun beforeChildren(current: LLVMValueRef): Boolean = when (LLVMGetValueKind(current)) {
|
||||||
|
LLVMValueKind.LLVMGlobalAliasValueKind,
|
||||||
|
LLVMValueKind.LLVMGlobalVariableValueKind,
|
||||||
|
LLVMValueKind.LLVMFunctionValueKind -> {
|
||||||
|
result.add(current)
|
||||||
|
false // Skip children.
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user