[K/N][IR] Split Devirtualization onto phases

The analysis itself and applying its results to the IR
This commit is contained in:
Igor Chevdar
2021-03-22 18:12:12 +05:00
parent b11201be81
commit 9c6943b8c4
5 changed files with 44 additions and 41 deletions
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.backend.konan.ir.KonanIr
import org.jetbrains.kotlin.library.SerializedMetadata import org.jetbrains.kotlin.library.SerializedMetadata
import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_BRIDGE_METHOD import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_BRIDGE_METHOD
import org.jetbrains.kotlin.backend.konan.optimizations.Devirtualization import org.jetbrains.kotlin.backend.konan.optimizations.DevirtualizationAnalysis
import org.jetbrains.kotlin.backend.konan.optimizations.ExternalModulesDFG import org.jetbrains.kotlin.backend.konan.optimizations.ExternalModulesDFG
import org.jetbrains.kotlin.backend.konan.optimizations.ModuleDFG import org.jetbrains.kotlin.backend.konan.optimizations.ModuleDFG
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
@@ -462,7 +462,7 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
var externalModulesDFG: ExternalModulesDFG? = null var externalModulesDFG: ExternalModulesDFG? = null
lateinit var lifetimes: MutableMap<IrElement, Lifetime> lateinit var lifetimes: MutableMap<IrElement, Lifetime>
lateinit var codegenVisitor: CodeGeneratorVisitor lateinit var codegenVisitor: CodeGeneratorVisitor
var devirtualizationAnalysisResult: Devirtualization.AnalysisResult? = null var devirtualizationAnalysisResult: DevirtualizationAnalysis.AnalysisResult? = null
var referencedFunctions: Set<IrFunction>? = null var referencedFunctions: Set<IrFunction>? = null
@@ -374,10 +374,11 @@ internal val bitcodePhase = NamedCompilerPhase(
description = "LLVM Bitcode generation", description = "LLVM Bitcode generation",
lower = contextLLVMSetupPhase then lower = contextLLVMSetupPhase then
buildDFGPhase then buildDFGPhase then
devirtualizationPhase then devirtualizationAnalysisPhase then
redundantCoercionsCleaningPhase then
dcePhase then dcePhase then
removeRedundantCallsToFileInitializersPhase then removeRedundantCallsToFileInitializersPhase then
devirtualizationPhase then
redundantCoercionsCleaningPhase then
createLLVMDeclarationsPhase then createLLVMDeclarationsPhase then
ghaPhase then ghaPhase then
RTTIPhase then RTTIPhase then
@@ -464,6 +465,7 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) {
disableUnless(linkerPhase, config.produce.involvesLinkStage) disableUnless(linkerPhase, config.produce.involvesLinkStage)
disableIf(testProcessorPhase, getNotNull(KonanConfigKeys.GENERATE_TEST_RUNNER) == TestRunnerKind.NONE) disableIf(testProcessorPhase, getNotNull(KonanConfigKeys.GENERATE_TEST_RUNNER) == TestRunnerKind.NONE)
disableUnless(buildDFGPhase, getBoolean(KonanConfigKeys.OPTIMIZATION)) disableUnless(buildDFGPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
disableUnless(devirtualizationAnalysisPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
disableUnless(devirtualizationPhase, getBoolean(KonanConfigKeys.OPTIMIZATION)) disableUnless(devirtualizationPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
disableUnless(escapeAnalysisPhase, getBoolean(KonanConfigKeys.OPTIMIZATION)) disableUnless(escapeAnalysisPhase, getBoolean(KonanConfigKeys.OPTIMIZATION))
// Inline accessors only in optimized builds due to separate compilation and possibility to get broken // Inline accessors only in optimized builds due to separate compilation and possibility to get broken
@@ -121,13 +121,13 @@ internal val returnsInsertionPhase = makeKonanModuleOpPhase(
op = { context, irModule -> irModule.files.forEach { ReturnsInsertionLowering(context).lower(it) } } op = { context, irModule -> irModule.files.forEach { ReturnsInsertionLowering(context).lower(it) } }
) )
internal val devirtualizationPhase = makeKonanModuleOpPhase( internal val devirtualizationAnalysisPhase = makeKonanModuleOpPhase(
name = "Devirtualization", name = "DevirtualizationAnalysis",
description = "Devirtualization", description = "Devirtualization analysis",
prerequisite = setOf(buildDFGPhase), prerequisite = setOf(buildDFGPhase),
op = { context, irModule -> op = { context, _ ->
context.devirtualizationAnalysisResult = Devirtualization.run( context.devirtualizationAnalysisResult = DevirtualizationAnalysis.run(
irModule, context, context.moduleDFG!!, ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap()) context, context.moduleDFG!!, ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
) )
} }
) )
@@ -150,7 +150,7 @@ internal val IrFunction.longName: String
internal val dcePhase = makeKonanModuleOpPhase( internal val dcePhase = makeKonanModuleOpPhase(
name = "DCEPhase", name = "DCEPhase",
description = "Dead code elimination", description = "Dead code elimination",
prerequisite = setOf(devirtualizationPhase), prerequisite = setOf(devirtualizationAnalysisPhase),
op = { context, _ -> op = { context, _ ->
val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap()) val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
@@ -236,7 +236,7 @@ internal val dcePhase = makeKonanModuleOpPhase(
internal val removeRedundantCallsToFileInitializersPhase = makeKonanModuleOpPhase( internal val removeRedundantCallsToFileInitializersPhase = makeKonanModuleOpPhase(
name = "RemoveRedundantCallsToFileInitializersPhase", name = "RemoveRedundantCallsToFileInitializersPhase",
description = "Redundant file initializers calls removal", description = "Redundant file initializers calls removal",
prerequisite = setOf(devirtualizationPhase), prerequisite = setOf(devirtualizationAnalysisPhase),
op = { context, _ -> op = { context, _ ->
val moduleDFG = context.moduleDFG!! val moduleDFG = context.moduleDFG!!
val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap()) val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
@@ -259,8 +259,8 @@ internal val removeRedundantCallsToFileInitializersPhase = makeKonanModuleOpPhas
} }
} }
val rootSet = Devirtualization.computeRootSet(context, moduleDFG, externalModulesDFG).toSet() val rootSet = DevirtualizationAnalysis.computeRootSet(context, moduleDFG, externalModulesDFG).toSet()
context.irModule!!.transformChildrenVoid(object: IrElementTransformerVoid() { context.irModule!!.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitFunction(declaration: IrFunction): IrStatement { override fun visitFunction(declaration: IrFunction): IrStatement {
declaration.transformChildrenVoid(this) declaration.transformChildrenVoid(this)
if (declaration in functionsBeingCalledFromOtherFiles if (declaration in functionsBeingCalledFromOtherFiles
@@ -278,10 +278,25 @@ internal val removeRedundantCallsToFileInitializersPhase = makeKonanModuleOpPhas
} }
) )
internal val devirtualizationPhase = makeKonanModuleOpPhase(
name = "Devirtualization",
description = "Devirtualization",
prerequisite = setOf(buildDFGPhase, devirtualizationAnalysisPhase),
op = { context, irModule ->
val devirtualizedCallSites =
context.devirtualizationAnalysisResult!!.devirtualizedCallSites
.asSequence()
.filter { it.key.irCallSite != null }
.associate { it.key.irCallSite!! to it.value }
DevirtualizationAnalysis.devirtualize(irModule, context,
ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap()), devirtualizedCallSites)
}
)
internal val escapeAnalysisPhase = makeKonanModuleOpPhase( internal val escapeAnalysisPhase = makeKonanModuleOpPhase(
name = "EscapeAnalysis", name = "EscapeAnalysis",
description = "Escape analysis", description = "Escape analysis",
prerequisite = setOf(buildDFGPhase, devirtualizationPhase), prerequisite = setOf(buildDFGPhase, devirtualizationAnalysisPhase),
op = { context, _ -> op = { context, _ ->
val entryPoint = context.ir.symbols.entryPoint?.owner val entryPoint = context.ir.symbols.entryPoint?.owner
val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap()) val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap())
@@ -314,7 +329,7 @@ internal val escapeAnalysisPhase = makeKonanModuleOpPhase(
internal val localEscapeAnalysisPhase = makeKonanModuleOpPhase( internal val localEscapeAnalysisPhase = makeKonanModuleOpPhase(
name = "LocalEscapeAnalysis", name = "LocalEscapeAnalysis",
description = "Local escape analysis", description = "Local escape analysis",
prerequisite = setOf(buildDFGPhase, devirtualizationPhase), prerequisite = setOf(buildDFGPhase, devirtualizationAnalysisPhase),
op = { context, _ -> op = { context, _ ->
LocalEscapeAnalysis.computeLifetimes(context, context.moduleDFG!!, context.lifetimes) LocalEscapeAnalysis.computeLifetimes(context, context.moduleDFG!!, context.lifetimes)
} }
@@ -55,7 +55,7 @@ internal class CallGraphBuilder(
val context: Context, val context: Context,
val moduleDFG: ModuleDFG, val moduleDFG: ModuleDFG,
val externalModulesDFG: ExternalModulesDFG, val externalModulesDFG: ExternalModulesDFG,
val devirtualizationAnalysisResult: Devirtualization.AnalysisResult, val devirtualizationAnalysisResult: DevirtualizationAnalysis.AnalysisResult,
val nonDevirtualizedCallSitesUnfoldFactor: Int val nonDevirtualizedCallSitesUnfoldFactor: Int
) { ) {
@@ -77,7 +77,7 @@ internal class CallGraphBuilder(
private val functionStack = mutableListOf<HandleFunctionParams>() private val functionStack = mutableListOf<HandleFunctionParams>()
fun build(): CallGraph { fun build(): CallGraph {
val rootSet = Devirtualization.computeRootSet(context, moduleDFG, externalModulesDFG) val rootSet = DevirtualizationAnalysis.computeRootSet(context, moduleDFG, externalModulesDFG)
for (symbol in rootSet) { for (symbol in rootSet) {
val function = moduleDFG.functions[symbol] val function = moduleDFG.functions[symbol]
if (function == null) if (function == null)
@@ -24,16 +24,11 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrReturnableBlockSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.irCall import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.ir.util.explicitParameters import org.jetbrains.kotlin.ir.util.explicitParameters
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
@@ -52,7 +47,7 @@ inline fun BitSet.forEachBit(block: (Int) -> Unit) {
// Devirtualization analysis is performed using Variable Type Analysis algorithm. // Devirtualization analysis is performed using Variable Type Analysis algorithm.
// See http://web.cs.ucla.edu/~palsberg/tba/papers/sundaresan-et-al-oopsla00.pdf for details. // See http://web.cs.ucla.edu/~palsberg/tba/papers/sundaresan-et-al-oopsla00.pdf for details.
internal object Devirtualization { internal object DevirtualizationAnalysis {
private val TAKE_NAMES = false // Take fqNames for all functions and types (for debug purposes). private val TAKE_NAMES = false // Take fqNames for all functions and types (for debug purposes).
@@ -120,9 +115,9 @@ internal object Devirtualization {
private val VIRTUAL_TYPE_ID = 0 // Id of [DataFlowIR.Type.Virtual]. private val VIRTUAL_TYPE_ID = 0 // Id of [DataFlowIR.Type.Virtual].
internal class DevirtualizationAnalysis(val context: Context, internal class DevirtualizationAnalysisImpl(val context: Context,
val moduleDFG: ModuleDFG, val moduleDFG: ModuleDFG,
val externalModulesDFG: ExternalModulesDFG) { val externalModulesDFG: ExternalModulesDFG) {
private val entryPoint = context.ir.symbols.entryPoint?.owner private val entryPoint = context.ir.symbols.entryPoint?.owner
@@ -1265,22 +1260,13 @@ internal object Devirtualization {
class DevirtualizedCallSite(val callee: DataFlowIR.FunctionSymbol, val possibleCallees: List<DevirtualizedCallee>) class DevirtualizedCallSite(val callee: DataFlowIR.FunctionSymbol, val possibleCallees: List<DevirtualizedCallee>)
class AnalysisResult(val devirtualizedCallSites: Map<DataFlowIR.Node.VirtualCall, DevirtualizedCallSite>, class AnalysisResult(val devirtualizedCallSites: Map<DataFlowIR.Node.VirtualCall, DevirtualizedCallSite>,
val typeHierarchy: DevirtualizationAnalysis.TypeHierarchy) val typeHierarchy: DevirtualizationAnalysisImpl.TypeHierarchy)
fun run(irModule: IrModuleFragment, context: Context, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG) fun run(context: Context, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG) =
: AnalysisResult { DevirtualizationAnalysisImpl(context, moduleDFG, externalModulesDFG).analyze()
val devirtualizationAnalysisResult = DevirtualizationAnalysis(context, moduleDFG, externalModulesDFG).analyze()
val devirtualizedCallSites =
devirtualizationAnalysisResult.devirtualizedCallSites
.asSequence()
.filter { it.key.irCallSite != null }
.associate { it.key.irCallSite!! to it.value }
devirtualize(irModule, context, externalModulesDFG, devirtualizedCallSites)
return devirtualizationAnalysisResult
}
private fun devirtualize(irModule: IrModuleFragment, context: Context, externalModulesDFG: ExternalModulesDFG, fun devirtualize(irModule: IrModuleFragment, context: Context, externalModulesDFG: ExternalModulesDFG,
devirtualizedCallSites: Map<IrCall, DevirtualizedCallSite>) { devirtualizedCallSites: Map<IrCall, DevirtualizedCallSite>) {
val symbols = context.ir.symbols val symbols = context.ir.symbols
val nativePtrEqualityOperatorSymbol = symbols.areEqualByValue[PrimitiveBinaryType.POINTER]!! val nativePtrEqualityOperatorSymbol = symbols.areEqualByValue[PrimitiveBinaryType.POINTER]!!
val optimize = context.shouldOptimize() val optimize = context.shouldOptimize()