[JS IR DCE] Print reachability info
This commit is contained in:
@@ -16,9 +16,13 @@ import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import java.util.*
|
||||
|
||||
private val PRINT_REACHABILITY_INFO = java.lang.Boolean.getBoolean("kotlin.js.ir.dce.print.reachability.info")
|
||||
|
||||
fun eliminateDeadDeclarations(
|
||||
module: IrModuleFragment,
|
||||
context: JsIrBackendContext,
|
||||
@@ -95,43 +99,64 @@ private fun removeUselessDeclarations(module: IrModuleFragment, usefulDeclaratio
|
||||
}
|
||||
|
||||
fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendContext): Set<IrDeclaration> {
|
||||
val reachabilityInfo: MutableSet<String> = if (PRINT_REACHABILITY_INFO) linkedSetOf() else Collections.emptySet()
|
||||
|
||||
val queue = ArrayDeque<IrDeclaration>()
|
||||
val result = hashSetOf<IrDeclaration>()
|
||||
val constructedClasses = hashSetOf<IrClass>()
|
||||
|
||||
fun IrDeclaration.enqueue() {
|
||||
if ((this !is IrProperty || this.isExternal) && this !in result) {
|
||||
result.add(this)
|
||||
queue.addLast(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun Iterable<IrDeclaration>.withNested(): Iterable<IrDeclaration> {
|
||||
val result = mutableListOf<IrDeclaration>()
|
||||
|
||||
this.forEach {
|
||||
it.acceptVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitBody(body: IrBody) {
|
||||
// Skip
|
||||
}
|
||||
|
||||
override fun visitDeclaration(declaration: IrDeclaration) {
|
||||
super.visitDeclaration(declaration)
|
||||
result += declaration
|
||||
}
|
||||
})
|
||||
fun IrDeclaration.enqueue(
|
||||
from: IrDeclaration?,
|
||||
description: String?,
|
||||
altFromFqn: String? = null
|
||||
) {
|
||||
if (this is IrProperty && !this.isExternal ||
|
||||
this in result
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
return result
|
||||
if (PRINT_REACHABILITY_INFO) {
|
||||
val fromFqn = (from as? IrDeclarationWithName)?.fqNameWhenAvailable?.asString() ?: altFromFqn ?: "<unknown>"
|
||||
|
||||
val toFqn = (this as? IrDeclarationWithName)?.fqNameWhenAvailable?.asString() ?: "<unknown>"
|
||||
|
||||
val v = "\"$fromFqn\" -> \"$toFqn\"" + (if (description.isNullOrBlank()) "" else " // $description")
|
||||
|
||||
reachabilityInfo.add(v)
|
||||
}
|
||||
|
||||
result.add(this)
|
||||
queue.addLast(this)
|
||||
}
|
||||
|
||||
// Add roots, including nested declarations
|
||||
// use withInitialIr to avoid ConcurrentModificationException in dce-driven lowering when adding roots' nested declarations (members)
|
||||
stageController.withInitialIr {
|
||||
roots.withNested().forEach { it.enqueue() }
|
||||
// Add roots
|
||||
roots.forEach {
|
||||
it.enqueue(null, null, altFromFqn = "<ROOT>")
|
||||
}
|
||||
|
||||
// Add roots' nested declarations
|
||||
roots.forEach {
|
||||
it.acceptVoid(
|
||||
object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitBody(body: IrBody) {
|
||||
// Skip
|
||||
}
|
||||
|
||||
override fun visitDeclaration(declaration: IrDeclaration) {
|
||||
if (declaration !== it) declaration.enqueue(it, "roots' nested declaration")
|
||||
|
||||
super.visitDeclaration(declaration)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val toStringMethod =
|
||||
@@ -148,29 +173,33 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendConte
|
||||
// TODO remove?
|
||||
stageController.lazyLower(declaration)
|
||||
|
||||
fun IrDeclaration.enqueue(description: String) {
|
||||
enqueue(declaration, description)
|
||||
}
|
||||
|
||||
if (declaration is IrClass) {
|
||||
declaration.superTypes.forEach {
|
||||
(it.classifierOrNull as? IrClassSymbol)?.owner?.enqueue()
|
||||
(it.classifierOrNull as? IrClassSymbol)?.owner?.enqueue("superTypes")
|
||||
}
|
||||
|
||||
// TODO find out how `doResume` gets removed
|
||||
if (declaration.symbol == context.ir.symbols.coroutineImpl) {
|
||||
declaration.declarations.forEach {
|
||||
if (it is IrSimpleFunction && it.name.asString() == "doResume") {
|
||||
it.enqueue()
|
||||
it.enqueue("hack for CoroutineImpl::doResume")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (declaration is IrSimpleFunction) {
|
||||
declaration.resolveFakeOverride()?.enqueue()
|
||||
if (declaration is IrSimpleFunction && declaration.isFakeOverride) {
|
||||
declaration.resolveFakeOverride()?.enqueue("real overridden fun")
|
||||
}
|
||||
|
||||
// Collect instantiated classes.
|
||||
if (declaration is IrConstructor) {
|
||||
declaration.constructedClass.let {
|
||||
it.enqueue()
|
||||
it.enqueue("constructed class")
|
||||
constructedClasses += it
|
||||
}
|
||||
}
|
||||
@@ -193,19 +222,19 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendConte
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression) {
|
||||
super.visitFunctionAccess(expression)
|
||||
|
||||
expression.symbol.owner.enqueue()
|
||||
expression.symbol.owner.enqueue("function access")
|
||||
}
|
||||
|
||||
override fun visitVariableAccess(expression: IrValueAccessExpression) {
|
||||
super.visitVariableAccess(expression)
|
||||
|
||||
expression.symbol.owner.enqueue()
|
||||
expression.symbol.owner.enqueue("variable access")
|
||||
}
|
||||
|
||||
override fun visitFieldAccess(expression: IrFieldAccessExpression) {
|
||||
super.visitFieldAccess(expression)
|
||||
|
||||
expression.symbol.owner.enqueue()
|
||||
expression.symbol.owner.enqueue("field access")
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall) {
|
||||
@@ -215,28 +244,28 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendConte
|
||||
context.intrinsics.jsBoxIntrinsic -> {
|
||||
val inlineClass = expression.getTypeArgument(0)!!.getInlinedClass()!!
|
||||
val constructor = inlineClass.declarations.filterIsInstance<IrConstructor>().single { it.isPrimary }
|
||||
constructor.enqueue()
|
||||
constructor.enqueue("intrinsic: jsBoxIntrinsic")
|
||||
}
|
||||
context.intrinsics.jsClass -> {
|
||||
(expression.getTypeArgument(0)!!.classifierOrFail.owner as IrDeclaration).enqueue()
|
||||
(expression.getTypeArgument(0)!!.classifierOrFail.owner as IrDeclaration).enqueue("intrinsic: jsClass")
|
||||
}
|
||||
context.intrinsics.jsObjectCreate.symbol -> {
|
||||
val classToCreate = expression.getTypeArgument(0)!!.classifierOrFail.owner as IrClass
|
||||
classToCreate.enqueue()
|
||||
classToCreate.enqueue("intrinsic: jsObjectCreate")
|
||||
constructedClasses += classToCreate
|
||||
}
|
||||
context.intrinsics.jsEquals -> {
|
||||
equalsMethod.enqueue()
|
||||
equalsMethod.enqueue("intrinsic: jsEquals")
|
||||
}
|
||||
context.intrinsics.jsToString -> {
|
||||
toStringMethod.enqueue()
|
||||
toStringMethod.enqueue("intrinsic: jsToString")
|
||||
}
|
||||
context.intrinsics.jsHashCode -> {
|
||||
hashCodeMethod.enqueue()
|
||||
hashCodeMethod.enqueue("intrinsic: jsHashCode")
|
||||
}
|
||||
context.intrinsics.jsPlus -> {
|
||||
if (expression.getValueArgument(0)?.type?.classOrNull == context.irBuiltIns.stringClass) {
|
||||
toStringMethod.enqueue()
|
||||
toStringMethod.enqueue("intrinsic: jsPlus")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -245,17 +274,23 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendConte
|
||||
override fun visitStringConcatenation(expression: IrStringConcatenation) {
|
||||
super.visitStringConcatenation(expression)
|
||||
|
||||
toStringMethod.enqueue()
|
||||
toStringMethod.enqueue("string concatenation")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun IrOverridableDeclaration<*>.overridesUsefulFunction(): Boolean {
|
||||
return this.overriddenSymbols.any {
|
||||
(it.owner as? IrOverridableDeclaration<*>)?.let {
|
||||
it in result || it.overridesUsefulFunction()
|
||||
} ?: false
|
||||
fun IrOverridableDeclaration<*>.findOverriddenUsefulDeclaration(): IrOverridableDeclaration<*>? {
|
||||
for (overriddenSymbol in this.overriddenSymbols) {
|
||||
val overriddenDeclaration = overriddenSymbol.owner as? IrOverridableDeclaration<*> ?: continue
|
||||
|
||||
if (overriddenDeclaration in result) return overriddenDeclaration
|
||||
|
||||
overriddenDeclaration.findOverriddenUsefulDeclaration()?.let {
|
||||
return it
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
for (klass in constructedClasses) {
|
||||
@@ -263,12 +298,14 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendConte
|
||||
for (declaration in ArrayList(klass.declarations)) {
|
||||
if (declaration in result) continue
|
||||
|
||||
if (declaration is IrOverridableDeclaration<*> && declaration.overridesUsefulFunction()) {
|
||||
declaration.enqueue()
|
||||
if (declaration is IrOverridableDeclaration<*>) {
|
||||
declaration.findOverriddenUsefulDeclaration()?.let {
|
||||
declaration.enqueue(it, "overrides useful declaration")
|
||||
}
|
||||
}
|
||||
|
||||
if (declaration is IrSimpleFunction && declaration.getJsNameOrKotlinName().asString() == "valueOf") {
|
||||
declaration.enqueue()
|
||||
declaration.enqueue(klass, "valueOf")
|
||||
}
|
||||
|
||||
// A hack to support `toJson` and other js-specific members
|
||||
@@ -276,7 +313,7 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendConte
|
||||
declaration is IrField && declaration.correspondingPropertySymbol?.owner?.getJsName() != null ||
|
||||
declaration is IrSimpleFunction && declaration.correspondingPropertySymbol?.owner?.getJsName() != null
|
||||
) {
|
||||
declaration.enqueue()
|
||||
declaration.enqueue(klass, "annotated by @JsName")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,8 +321,12 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendConte
|
||||
for (declaration in ArrayList(klass.declarations)) {
|
||||
// TODO this is a hack.
|
||||
if (declaration is IrProperty) {
|
||||
declaration.getter?.let { if (it.overridesUsefulFunction()) it.enqueue() }
|
||||
declaration.setter?.let { if (it.overridesUsefulFunction()) it.enqueue() }
|
||||
fun IrSimpleFunction.enqueue(description: String) {
|
||||
findOverriddenUsefulDeclaration()?.let { enqueue(it, description) }
|
||||
}
|
||||
|
||||
declaration.getter?.enqueue("(getter) overrides useful declaration")
|
||||
declaration.setter?.enqueue("(setter) overrides useful declaration")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,12 +335,16 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendConte
|
||||
ArrayList(klass.declarations).forEach {
|
||||
// TODO: fix the heck
|
||||
// if (it is IrSimpleFunction && it.name.asString() == "doResume") {
|
||||
it.enqueue()
|
||||
it.enqueue(klass, "hack for CoroutineImpl::doResume")
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (PRINT_REACHABILITY_INFO) {
|
||||
reachabilityInfo.forEach(::println)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user