IR: remove FunctionLoweringPass, refactor usages to FileLoweringPass

FunctionLoweringPass didn't add much value over FileLoweringPass, but
had a hidden footgun (like ClassLoweringPass) in that if your lowering
pass invoked a visitor/transformer on the function body and you forgot
to override visitFunction to stop visiting nested functions,
runOnFilePostfix would work with the complexity of the squared number of
nesting levels. It looks like this was happening with K/N's
DataClassOperatorsLowering (I haven't measured though).

(cherry picked from commit 822410a647560826e8cce96921eae32f237cf335)
This commit is contained in:
Alexander Udalov
2020-10-12 14:37:53 +02:00
committed by Vasily Levchenko
parent 306fd2ae07
commit 297361f0c8
2 changed files with 62 additions and 86 deletions
@@ -5,73 +5,76 @@
package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.FunctionLoweringPass
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlock
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.ir.util.isSimpleTypeWithQuestionMark
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
internal class DataClassOperatorsLowering(val context: Context): FunctionLoweringPass {
import org.jetbrains.kotlin.ir.util.isSimpleTypeWithQuestionMark
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
internal class DataClassOperatorsLowering(val context: Context) : FileLoweringPass, IrElementTransformer<IrFunction?> {
private val irBuiltins = context.irModule!!.irBuiltins
override fun lower(irFunction: IrFunction) {
irFunction.transformChildrenVoid(object: IrElementTransformerVoid() {
override fun visitCall(expression: IrCall): IrExpression {
expression.transformChildrenVoid(this)
override fun lower(irFile: IrFile) {
irFile.transformChildren(this, null)
}
if (expression.symbol != irBuiltins.dataClassArrayMemberToStringSymbol
&& expression.symbol != irBuiltins.dataClassArrayMemberHashCodeSymbol)
return expression
override fun visitFunction(declaration: IrFunction, data: IrFunction?): IrStatement =
super.visitFunction(declaration, declaration)
val argument = expression.getValueArgument(0)!!
val argumentClassifier = argument.type.classifierOrFail
override fun visitCall(expression: IrCall, data: IrFunction?): IrExpression {
expression.transformChildren(this, data)
val isToString = expression.symbol == irBuiltins.dataClassArrayMemberToStringSymbol
val newCalleeSymbol = if (isToString)
context.ir.symbols.arrayContentToString[argumentClassifier]!!
else
context.ir.symbols.arrayContentHashCode[argumentClassifier]!!
if (expression.symbol != irBuiltins.dataClassArrayMemberToStringSymbol
&& expression.symbol != irBuiltins.dataClassArrayMemberHashCodeSymbol)
return expression
val newCallee = newCalleeSymbol.owner
val argument = expression.getValueArgument(0)!!
val argumentClassifier = argument.type.classifierOrFail
val startOffset = expression.startOffset
val endOffset = expression.endOffset
val irBuilder = context.createIrBuilder(irFunction.symbol, startOffset, endOffset)
val isToString = expression.symbol == irBuiltins.dataClassArrayMemberToStringSymbol
val newCalleeSymbol = if (isToString)
context.ir.symbols.arrayContentToString[argumentClassifier]!!
else
context.ir.symbols.arrayContentHashCode[argumentClassifier]!!
return irBuilder.run {
// TODO: use more precise type arguments.
val typeArguments = (0 until newCallee.typeParameters.size).map { irBuiltins.anyNType }
val newCallee = newCalleeSymbol.owner
if (!argument.type.isSimpleTypeWithQuestionMark) {
irCall(newCallee, typeArguments).apply {
extensionReceiver = argument
}
} else {
irBlock(argument) {
val tmp = irTemporary(argument)
val call = irCall(newCallee, typeArguments).apply {
extensionReceiver = irGet(tmp)
}
+irIfThenElse(call.type,
irEqeqeq(irGet(tmp), irNull()),
if (isToString)
irString("null")
else
irInt(0),
call)
}
val startOffset = expression.startOffset
val endOffset = expression.endOffset
val irBuilder = context.createIrBuilder(data!!.symbol, startOffset, endOffset)
return irBuilder.run {
// TODO: use more precise type arguments.
val typeArguments = (0 until newCallee.typeParameters.size).map { irBuiltins.anyNType }
if (!argument.type.isSimpleTypeWithQuestionMark) {
irCall(newCallee, typeArguments).apply {
extensionReceiver = argument
}
} else {
irBlock(argument) {
val tmp = irTemporary(argument)
val call = irCall(newCallee, typeArguments).apply {
extensionReceiver = irGet(tmp)
}
+irIfThenElse(call.type,
irEqeqeq(irGet(tmp), irNull()),
if (isToString)
irString("null")
else
irInt(0),
call)
}
}
})
}
}
}
}
@@ -6,59 +6,34 @@
package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FunctionLoweringPass
import org.jetbrains.kotlin.backend.common.lower.at
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlock
import org.jetbrains.kotlin.ir.util.isSimpleTypeWithQuestionMark
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.konan.ir.containsNull
import org.jetbrains.kotlin.backend.konan.ir.isSubtypeOf
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.makeNotNull
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.util.isSimpleTypeWithQuestionMark
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
/**
* This lowering pass lowers some [IrTypeOperatorCall]s.
*/
internal class TypeOperatorLowering(val context: CommonBackendContext) : FunctionLoweringPass {
override fun lower(irFunction: IrFunction) {
val transformer = TypeOperatorTransformer(context, irFunction.symbol)
irFunction.transformChildrenVoid(transformer)
}
}
private class TypeOperatorTransformer(val context: CommonBackendContext, val function: IrFunctionSymbol) : IrElementTransformerVoid() {
private val builder = context.createIrBuilder(function)
val throwNullPointerException = context.ir.symbols.throwNullPointerException
override fun visitFunction(declaration: IrFunction): IrStatement {
// ignore inner functions during this pass
return declaration
internal class TypeOperatorLowering(val context: CommonBackendContext) : FileLoweringPass, IrBuildingTransformer(context) {
override fun lower(irFile: IrFile) {
irFile.transformChildren(this, null)
}
private fun IrType.erasure(): IrType {
if (this !is IrSimpleType) return this
val classifier = this.classifier
return when (classifier) {
return when (val classifier = classifier) {
is IrClassSymbol -> this
is IrTypeParameterSymbol -> {
val upperBound = classifier.owner.superTypes.firstOrNull() ?:
@@ -87,7 +62,7 @@ private class TypeOperatorTransformer(val context: CommonBackendContext, val fun
expression.argument.type.isSubtypeOf(typeOperand) -> expression.argument
expression.argument.type.containsNull() -> {
with (builder) {
with(builder) {
irLetS(expression.argument) { argument ->
irIfThenElse(
type = expression.type,
@@ -96,7 +71,7 @@ private class TypeOperatorTransformer(val context: CommonBackendContext, val fun
thenPart = if (typeOperand.isSimpleTypeWithQuestionMark)
irNull()
else
irCall(throwNullPointerException.owner),
irCall(this@TypeOperatorLowering.context.ir.symbols.throwNullPointerException.owner),
elsePart = irAs(irGet(argument.owner), typeOperand.makeNotNull())
)
@@ -112,8 +87,6 @@ private class TypeOperatorTransformer(val context: CommonBackendContext, val fun
}
}
private fun KotlinType.isNullable() = TypeUtils.isNullableType(this)
private fun lowerSafeCast(expression: IrTypeOperatorCall): IrExpression {
val typeOperand = expression.typeOperand.erasure()
@@ -136,4 +109,4 @@ private class TypeOperatorTransformer(val context: CommonBackendContext, val fun
else -> expression
}
}
}
}