[JS BE] Implement accessor inlining optimization (WIP)
- enable constant folding
This commit is contained in:
+1
-1
@@ -51,7 +51,7 @@ abstract class InitializersLoweringBase(open val context: CommonBackendContext)
|
|||||||
irClass.declarations.removeAll { it is IrAnonymousInitializer && filter(it) }
|
irClass.declarations.removeAll { it is IrAnonymousInitializer && filter(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun shouldEraseFieldInitializer(irField: IrField): Boolean = true
|
protected open fun shouldEraseFieldInitializer(irField: IrField): Boolean = irField.correspondingPropertySymbol?.owner?.isConst != true
|
||||||
|
|
||||||
private fun handleField(irClass: IrClass, declaration: IrField): IrStatement? =
|
private fun handleField(irClass: IrClass, declaration: IrField): IrStatement? =
|
||||||
declaration.initializer?.run {
|
declaration.initializer?.run {
|
||||||
|
|||||||
+134
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 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.backend.common.lower.optimizations
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||||
|
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||||
|
import org.jetbrains.kotlin.backend.common.ir.isTopLevel
|
||||||
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
|
||||||
|
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
|
||||||
|
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||||
|
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
|
||||||
|
class PropertyAccessorInlineLowering(private val context: CommonBackendContext) : FileLoweringPass {
|
||||||
|
|
||||||
|
private val IrProperty.isSafeToInline: Boolean get() = isTopLevel || (modality === Modality.FINAL || visibility == Visibilities.PRIVATE) || (parent as IrClass).modality === Modality.FINAL
|
||||||
|
|
||||||
|
// TODO: implement general function inlining optimization and replace it with
|
||||||
|
private inner class AccessorInliner : IrElementTransformerVoid() {
|
||||||
|
|
||||||
|
private val unitType = context.irBuiltIns.unitType
|
||||||
|
|
||||||
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
|
expression.transformChildrenVoid(this)
|
||||||
|
|
||||||
|
val callee = expression.symbol.owner as IrSimpleFunction
|
||||||
|
val property = callee.correspondingPropertySymbol?.owner ?: return expression
|
||||||
|
|
||||||
|
// Some devirtualization required here
|
||||||
|
if (!property.isSafeToInline) return expression
|
||||||
|
|
||||||
|
val parent = property.parent
|
||||||
|
if (parent is IrClass) {
|
||||||
|
// TODO: temporary workarounds
|
||||||
|
if (parent.isExpect || property.isExpect) return expression
|
||||||
|
if (parent.parent is IrExternalPackageFragment) return expression
|
||||||
|
if (parent.isInline) return expression
|
||||||
|
}
|
||||||
|
if (property.isEffectivelyExternal()) return expression
|
||||||
|
|
||||||
|
if (property.isConst) {
|
||||||
|
val initializer =
|
||||||
|
(property.backingField?.initializer ?: error("Constant property has to have a backing field with initializer"))
|
||||||
|
return initializer.expression.deepCopyWithSymbols()
|
||||||
|
}
|
||||||
|
|
||||||
|
val backingField = property.backingField ?: return expression
|
||||||
|
|
||||||
|
if (property.getter === callee) {
|
||||||
|
return tryInlineSimpleGetter(expression, callee, backingField) ?: expression
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property.setter === callee) {
|
||||||
|
return tryInlineSimpleSetter(expression, callee, backingField) ?: expression
|
||||||
|
}
|
||||||
|
|
||||||
|
return expression
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun tryInlineSimpleGetter(call: IrCall, callee: IrSimpleFunction, backingField: IrField): IrExpression? {
|
||||||
|
if (!isSimpleGetter(callee, backingField)) return null
|
||||||
|
|
||||||
|
return call.run {
|
||||||
|
IrGetFieldImpl(startOffset, endOffset, backingField.symbol, backingField.type, call.dispatchReceiver, origin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSimpleGetter(callee: IrSimpleFunction, backingField: IrField): Boolean {
|
||||||
|
val body = callee.body?.let { it as IrBlockBody } ?: return false
|
||||||
|
|
||||||
|
val stmt = body.statements.singleOrNull() ?: return false
|
||||||
|
val returnStmt = stmt as? IrReturn ?: return false
|
||||||
|
val getFieldStmt = returnStmt.value as? IrGetField ?: return false
|
||||||
|
if (getFieldStmt.symbol !== backingField.symbol) return false
|
||||||
|
val receiver = getFieldStmt.receiver
|
||||||
|
|
||||||
|
if (receiver == null) {
|
||||||
|
assert(callee.dispatchReceiverParameter == null)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receiver is IrGetValue) return receiver.symbol.owner === callee.dispatchReceiverParameter
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun tryInlineSimpleSetter(call: IrCall, callee: IrSimpleFunction, backingField: IrField): IrExpression? {
|
||||||
|
if (!isSimpleSetter(callee, backingField)) return null
|
||||||
|
|
||||||
|
return call.run {
|
||||||
|
val value = getValueArgument(0) ?: error("Setter should have a value argument")
|
||||||
|
IrSetFieldImpl(startOffset, endOffset, backingField.symbol, call.dispatchReceiver, value, unitType, origin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSimpleSetter(callee: IrSimpleFunction, backingField: IrField): Boolean {
|
||||||
|
val body = callee.body?.let { it as IrBlockBody } ?: return false
|
||||||
|
|
||||||
|
val stmt = body.statements.singleOrNull() ?: return false
|
||||||
|
val setFieldStmt = stmt as? IrSetField ?: return false
|
||||||
|
if (setFieldStmt.symbol !== backingField.symbol) return false
|
||||||
|
|
||||||
|
// TODO: support constant setters
|
||||||
|
val setValue = setFieldStmt.value as? IrGetValue ?: return false
|
||||||
|
val valueSymbol = callee.valueParameters.single().symbol
|
||||||
|
if (setValue.symbol !== valueSymbol) return false
|
||||||
|
|
||||||
|
val receiver = setFieldStmt.receiver
|
||||||
|
|
||||||
|
if (receiver == null) {
|
||||||
|
assert(callee.dispatchReceiverParameter == null)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receiver is IrGetValue) return receiver.symbol.owner === callee.dispatchReceiverParameter
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun lower(irFile: IrFile) {
|
||||||
|
irFile.transformChildrenVoid(AccessorInliner())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,8 @@ import org.jetbrains.kotlin.backend.common.*
|
|||||||
import org.jetbrains.kotlin.backend.common.lower.*
|
import org.jetbrains.kotlin.backend.common.lower.*
|
||||||
import org.jetbrains.kotlin.backend.common.lower.inline.FunctionInlining
|
import org.jetbrains.kotlin.backend.common.lower.inline.FunctionInlining
|
||||||
import org.jetbrains.kotlin.backend.common.lower.loops.ForLoopsLowering
|
import org.jetbrains.kotlin.backend.common.lower.loops.ForLoopsLowering
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.optimizations.FoldConstantLowering
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.optimizations.PropertyAccessorInlineLowering
|
||||||
import org.jetbrains.kotlin.backend.common.phaser.*
|
import org.jetbrains.kotlin.backend.common.phaser.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.*
|
import org.jetbrains.kotlin.ir.backend.js.lower.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.calls.CallsLowering
|
import org.jetbrains.kotlin.ir.backend.js.lower.calls.CallsLowering
|
||||||
@@ -161,7 +163,20 @@ private val returnableBlockLoweringPhase = makeJsModulePhase(
|
|||||||
private val forLoopsLoweringPhase = makeJsModulePhase(
|
private val forLoopsLoweringPhase = makeJsModulePhase(
|
||||||
::ForLoopsLowering,
|
::ForLoopsLowering,
|
||||||
name = "ForLoopsLowering",
|
name = "ForLoopsLowering",
|
||||||
description = "For loops lowering"
|
description = "[Optimization] For loops lowering"
|
||||||
|
)
|
||||||
|
|
||||||
|
private val propertyAccessorInlinerLoweringPhase = makeJsModulePhase(
|
||||||
|
::PropertyAccessorInlineLowering,
|
||||||
|
name = "PropertyAccessorInlineLowering",
|
||||||
|
description = "[Optimization] Inline property accessors"
|
||||||
|
)
|
||||||
|
|
||||||
|
private val foldConstantLoweringPhase = makeJsModulePhase(
|
||||||
|
::FoldConstantLowering,
|
||||||
|
name = "FoldConstantLowering",
|
||||||
|
description = "[Optimization] Constant Folding",
|
||||||
|
prerequisite = setOf(propertyAccessorInlinerLoweringPhase)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val localDelegatedPropertiesLoweringPhase = makeJsModulePhase(
|
private val localDelegatedPropertiesLoweringPhase = makeJsModulePhase(
|
||||||
@@ -418,6 +433,9 @@ val jsPhases = namedIrModulePhase(
|
|||||||
suspendFunctionsLoweringPhase then
|
suspendFunctionsLoweringPhase then
|
||||||
returnableBlockLoweringPhase then
|
returnableBlockLoweringPhase then
|
||||||
forLoopsLoweringPhase then
|
forLoopsLoweringPhase then
|
||||||
|
primitiveCompanionLoweringPhase then
|
||||||
|
propertyAccessorInlinerLoweringPhase then
|
||||||
|
foldConstantLoweringPhase then
|
||||||
privateMembersLoweringPhase then
|
privateMembersLoweringPhase then
|
||||||
callableReferenceLoweringPhase then
|
callableReferenceLoweringPhase then
|
||||||
defaultArgumentStubGeneratorPhase then
|
defaultArgumentStubGeneratorPhase then
|
||||||
@@ -436,7 +454,6 @@ val jsPhases = namedIrModulePhase(
|
|||||||
inlineClassLoweringPhase then
|
inlineClassLoweringPhase then
|
||||||
autoboxingTransformerPhase then
|
autoboxingTransformerPhase then
|
||||||
blockDecomposerLoweringPhase then
|
blockDecomposerLoweringPhase then
|
||||||
primitiveCompanionLoweringPhase then
|
|
||||||
constLoweringPhase then
|
constLoweringPhase then
|
||||||
objectDeclarationLoweringPhase then
|
objectDeclarationLoweringPhase then
|
||||||
objectUsageLoweringPhase then
|
objectUsageLoweringPhase then
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
// IGNORE_BACKEND_FIR: JVM_IR
|
||||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||||
// IGNORE_BACKEND: JS
|
// IGNORE_BACKEND: JS
|
||||||
|
// IGNORE_BACKEND: JS_IR
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
if (239.toByte().toString() != (239.toByte() as Byte?).toString()) return "byte failed"
|
if (239.toByte().toString() != (239.toByte() as Byte?).toString()) return "byte failed"
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
|
||||||
// IGNORE_BACKEND: JS
|
// IGNORE_BACKEND: JS
|
||||||
|
|
||||||
class A() {
|
class A() {
|
||||||
|
|||||||
Reference in New Issue
Block a user