Add constant folding after FIR2IR processing

This commit is contained in:
Ivan Kylchik
2020-06-11 17:21:17 +03:00
parent f2045b857b
commit f20e878d35
3 changed files with 94 additions and 3 deletions
@@ -240,6 +240,10 @@ class Fir2IrConverter(
descriptor.owner.parent = parentClass ?: continue
}
irModuleFragment.files.forEach {
it.transformChildren(IrConstTransformer(irModuleFragment), null)
}
return Fir2IrResult(irModuleFragment, symbolTable, sourceManager, components)
}
}
@@ -0,0 +1,73 @@
/*
* Copyright 2010-2020 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.fir.backend
import org.jetbrains.kotlin.backend.common.interpreter.EvaluationMode
import org.jetbrains.kotlin.backend.common.interpreter.IrCompileTimeChecker
import org.jetbrains.kotlin.backend.common.interpreter.IrInterpreter
import org.jetbrains.kotlin.backend.common.interpreter.toIrConst
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrErrorExpression
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.isArray
import org.jetbrains.kotlin.ir.types.typeOrNull
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
class IrConstTransformer(irModuleFragment: IrModuleFragment) : IrElementTransformerVoid() {
private val interpreter = IrInterpreter(irModuleFragment)
private fun IrExpression.report(original: IrExpression): IrExpression {
if (this == original) return this
return if (this !is IrErrorExpression) this else original
}
override fun visitCall(expression: IrCall): IrExpression {
if (expression.accept(IrCompileTimeChecker(mode = EvaluationMode.ONLY_BUILTINS), null)) {
return interpreter.interpret(expression).report(expression)
}
return super.visitCall(expression)
}
override fun visitField(declaration: IrField): IrStatement {
val initializer = declaration.initializer
val expression = initializer?.expression ?: return declaration
if (expression is IrConst<*>) return declaration
val isCompileTimeComputable = expression.accept(IrCompileTimeChecker(declaration, mode = EvaluationMode.ONLY_BUILTINS), null)
val isConst = declaration.correspondingPropertySymbol?.owner?.isConst == true
if (isConst && !isCompileTimeComputable) {
//throw AssertionError("Const property is used only with functions annotated as CompileTimeCalculation: " + declaration.dump())
} else if (isCompileTimeComputable) {
initializer.expression = interpreter.interpret(expression).report(expression)
}
return declaration
}
override fun visitClass(declaration: IrClass): IrStatement {
declaration.annotations.forEach {
for (i in 0 until it.valueArgumentsCount) {
val arg = it.getValueArgument(i) ?: continue
if (arg.accept(IrCompileTimeChecker(mode = EvaluationMode.ONLY_BUILTINS), null)) {
val const = interpreter.interpret(arg).report(arg)
it.putValueArgument(i, const.convertToConstIfPossible(it.symbol.owner.valueParameters[i].type))
}
}
}
return super.visitClass(declaration)
}
private fun IrExpression.convertToConstIfPossible(type: IrType): IrExpression {
if (this !is IrConst<*>) return this
if (type.isArray()) return this.convertToConstIfPossible((type as IrSimpleType).arguments.single().typeOrNull!!)
return this.value.toIrConst(type, this.startOffset, this.endOffset)
}
}
@@ -13,6 +13,8 @@ import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.isPrimitiveType
import org.jetbrains.kotlin.ir.types.isString
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.name.FqName
@@ -32,9 +34,21 @@ class IrCompileTimeChecker(
private fun IrDeclaration.isContract() = isMarkedWith(contractsDslAnnotation)
private fun IrDeclaration.isMarkedAsEvaluateIntrinsic() = isMarkedWith(evaluateIntrinsicAnnotation)
private fun IrDeclaration.isMarkedAsCompileTime(): Boolean = isMarkedWith(compileTimeAnnotation) ||
(this is IrSimpleFunction && this.isFakeOverride && this.overriddenSymbols.any { it.owner.isMarkedAsCompileTime() }) ||
(this.parent as? IrClass)?.fqNameWhenAvailable?.asString() in compileTimeTypeAliases
private fun IrDeclaration.isMarkedAsCompileTime(): Boolean {
if (mode == EvaluationMode.FULL)
return isMarkedWith(compileTimeAnnotation) ||
(this is IrSimpleFunction && this.isFakeOverride && this.overriddenSymbols.any { it.owner.isMarkedAsCompileTime() }) ||
this.parentClassOrNull?.fqNameWhenAvailable?.asString() in compileTimeTypeAliases
val parent = this.parentClassOrNull
val parentType = parent?.defaultType
return when {
parentType?.isPrimitiveType() == true -> (this as IrFunction).name.asString() !in setOf("inc", "dec", "rangeTo", "hashCode")
parentType?.isString() == true -> (this as IrDeclarationWithName).name.asString() !in setOf("subSequence", "hashCode")
parent?.isCompanion == true -> parent.parentClassOrNull?.defaultType?.let { it.isPrimitiveType() || it.isUnsigned() } == true
else -> false
}
}
private fun IrDeclaration.isMarkedWith(annotation: FqName): Boolean {
if (this is IrClass && this.isCompanion) return false