All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.ir.backend.js.ic.InlineFunctionTransitiveHashCalculator.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/*
 * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
 */

package org.jetbrains.kotlin.ir.backend.js.ic

import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.isFakeOverride
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid


class InlineFunctionTransitiveHashCalculator {
    private val flatHashes = mutableMapOf()
    private val inlineFunctionCallGraph: MutableMap> = mutableMapOf()
    private val processingFunctions = mutableSetOf()
    private val functionTransitiveHashes = mutableMapOf()
    private val idSignatureTransitiveHashes = mutableMapOf()

    val transitiveHashes: Map
        get() = idSignatureTransitiveHashes

    private inner class FlatHashCalculator : IrElementVisitorVoid {
        override fun visitElement(element: IrElement) = element.acceptChildren(this, null)

        override fun visitSimpleFunction(declaration: IrSimpleFunction) {
            if (declaration.isInline) {
                if (declaration in flatHashes) {
                    return
                }
                flatHashes[declaration] = declaration.irElementHashForIC()
            }
            // go deeper since local inline special declarations (like a reference adaptor) may appear
            declaration.acceptChildren(this, null)
        }
    }

    private inner class CallGraphBuilder : IrElementVisitor> {
        var inlineFunctionCallDepth: Int = 0

        override fun visitElement(element: IrElement, data: MutableSet) = element.acceptChildren(this, data)

        override fun visitSimpleFunction(declaration: IrSimpleFunction, data: MutableSet) {
            if (declaration in inlineFunctionCallGraph) {
                return
            }
            val newGraph = mutableSetOf()
            inlineFunctionCallGraph[declaration] = newGraph
            declaration.acceptChildren(this, newGraph)
        }

        override fun visitCall(expression: IrCall, data: MutableSet) {
            val callee = expression.symbol.owner
            if (callee.isInline) {
                // TODO: do not ignore fake overrides after KT-51896
                if (!callee.isFakeOverride) {
                    data += callee
                }
                inlineFunctionCallDepth += 1
            }
            expression.acceptChildren(this, data)
            if (callee.isInline) {
                inlineFunctionCallDepth -= 1
                if (inlineFunctionCallDepth < 0) {
                    icError("inline function calls depth inconsistent")
                }
            }
        }

        override fun visitFunctionReference(expression: IrFunctionReference, data: MutableSet) {
            val reference = expression.symbol.owner
            if (inlineFunctionCallDepth > 0 && reference.isInline) {
                // this if is fine, because fake overrides are not inlined as function reference calls even as inline function args
                if (!reference.isFakeOverride) {
                    data += reference
                }
            }
            expression.acceptChildren(this, data)
        }
    }

    private fun getInlineFunctionTransitiveHash(f: IrFunction): ICHash = functionTransitiveHashes.getOrPut(f) {
        if (!processingFunctions.add(f)) {
            icError("inline circle through function ${f.render()} detected")
        }
        val callees = inlineFunctionCallGraph[f] ?: icError("inline function is missed in inline graph ${f.render()}")
        val flatHash = flatHashes[f] ?: icError("no flat hash for ${f.render()}")
        var functionInlineHash = flatHash
        for (callee in callees) {
            functionInlineHash = functionInlineHash.combineWith(getInlineFunctionTransitiveHash(callee))
        }
        processingFunctions.remove(f)
        f.symbol.signature?.let { idSignatureTransitiveHashes[it] = functionInlineHash }
        functionInlineHash
    }

    private fun updateTransitiveHashesByCallGraph() {
        for ((f, callees) in inlineFunctionCallGraph.entries) {
            if (f.isInline) {
                getInlineFunctionTransitiveHash(f)
            } else {
                callees.forEach(::getInlineFunctionTransitiveHash)
            }
        }
    }

    fun updateTransitiveHashes(fragments: Collection) {
        fragments.forEach { it.acceptVoid(FlatHashCalculator()) }
        fragments.forEach { it.acceptChildren(CallGraphBuilder(), mutableSetOf()) }
        updateTransitiveHashesByCallGraph()
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy