[JS IR BE] Eliminate identical IrElements duplicates

This commit is contained in:
Svyatoslav Kuzmich
2018-11-19 16:48:15 +03:00
parent ba0f652e02
commit 2172a12df4
10 changed files with 97 additions and 90 deletions
@@ -19,10 +19,16 @@ import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
class UnitMaterializationLowering(context: BackendContext): FileLoweringPass { class UnitMaterializationLowering(context: BackendContext) : FileLoweringPass {
private val unitType = context.irBuiltIns.unitType private val unitType = context.irBuiltIns.unitType
private val unitValue = IrGetObjectValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, unitType, unitType.classifierOrFail as IrClassSymbol) private val unitValue
get() = IrGetObjectValueImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
unitType,
unitType.classifierOrFail as IrClassSymbol
)
override fun lower(irFile: IrFile) { override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() { irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
@@ -79,19 +79,19 @@ class BlockDecomposerLowering(context: JsIrBackendContext) : DeclarationContaine
} }
} }
class BlockDecomposerTransformer(context: JsIrBackendContext) : IrElementTransformerVoid() { class BlockDecomposerTransformer(private val context: JsIrBackendContext) : IrElementTransformerVoid() {
private lateinit var function: IrFunction private lateinit var function: IrFunction
private var tmpVarCounter: Int = 0 private var tmpVarCounter: Int = 0
private val statementTransformer = StatementTransformer() private val statementTransformer = StatementTransformer()
private val expressionTransformer = ExpressionTransformer() private val expressionTransformer = ExpressionTransformer()
private val constTrue = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true) private val constTrue get() = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true)
private val constFalse = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, false) private val constFalse get() = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, false)
private val nothingType = context.irBuiltIns.nothingNType private val nothingType = context.irBuiltIns.nothingNType
private val unitType = context.irBuiltIns.unitType private val unitType = context.irBuiltIns.unitType
private val unitValue = JsIrBuilder.buildGetObjectValue(unitType, context.symbolTable.referenceClass(context.builtIns.unit)) private val unitValue get() = JsIrBuilder.buildGetObjectValue(unitType, context.symbolTable.referenceClass(context.builtIns.unit))
private val unreachableFunction = context.intrinsics.unreachable private val unreachableFunction = context.intrinsics.unreachable
private val booleanNotSymbol = context.irBuiltIns.booleanNotSymbol private val booleanNotSymbol = context.irBuiltIns.booleanNotSymbol
@@ -43,9 +43,9 @@ data class CallableReferenceKey(
// TODO: generate $metadata$ property and fill it with corresponding KFunction/KProperty interface // TODO: generate $metadata$ property and fill it with corresponding KFunction/KProperty interface
class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringPass { class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringPass {
private val callableNameConst = JsIrBuilder.buildString(context.irBuiltIns.stringType, Namer.KCALLABLE_NAME) private val callableNameConst get() = JsIrBuilder.buildString(context.irBuiltIns.stringType, Namer.KCALLABLE_NAME)
private val getterConst = JsIrBuilder.buildString(context.irBuiltIns.stringType, Namer.KPROPERTY_GET) private val getterConst get() = JsIrBuilder.buildString(context.irBuiltIns.stringType, Namer.KPROPERTY_GET)
private val setterConst = JsIrBuilder.buildString(context.irBuiltIns.stringType, Namer.KPROPERTY_SET) private val setterConst get() = JsIrBuilder.buildString(context.irBuiltIns.stringType, Namer.KPROPERTY_SET)
private val callableToFactoryFunction = context.callableReferencesCache private val callableToFactoryFunction = context.callableReferencesCache
private val newDeclarations = mutableListOf<IrDeclaration>() private val newDeclarations = mutableListOf<IrDeclaration>()
@@ -327,13 +327,13 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
// //
val cacheName = "${factoryFunction.name}_${Namer.KCALLABLE_CACHE_SUFFIX}" val cacheName = "${factoryFunction.name}_${Namer.KCALLABLE_CACHE_SUFFIX}"
val type = factoryFunction.returnType val type = factoryFunction.returnType
val irNull = JsIrBuilder.buildNull(context.irBuiltIns.nothingNType) val irNull = { JsIrBuilder.buildNull(context.irBuiltIns.nothingNType) }
val cacheVar = JsIrBuilder.buildVar(type, factoryFunction.parent, cacheName, true, initializer = irNull) val cacheVar = JsIrBuilder.buildVar(type, factoryFunction.parent, cacheName, true, initializer = irNull())
val irCacheValue = JsIrBuilder.buildGetValue(cacheVar.symbol) val irCacheValue = { JsIrBuilder.buildGetValue(cacheVar.symbol) }
val irIfCondition = JsIrBuilder.buildCall(context.irBuiltIns.eqeqSymbol).apply { val irIfCondition = JsIrBuilder.buildCall(context.irBuiltIns.eqeqSymbol).apply {
putValueArgument(0, irCacheValue) putValueArgument(0, irCacheValue())
putValueArgument(1, irNull) putValueArgument(1, irNull())
} }
val irSetCache = val irSetCache =
JsIrBuilder.buildSetVariable(cacheVar.symbol, JsIrBuilder.buildGetValue(varSymbol), context.irBuiltIns.unitType) JsIrBuilder.buildSetVariable(cacheVar.symbol, JsIrBuilder.buildGetValue(varSymbol), context.irBuiltIns.unitType)
@@ -344,7 +344,7 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
val irThenBranch = JsIrBuilder.buildBlock(context.irBuiltIns.unitType, thenStatements) val irThenBranch = JsIrBuilder.buildBlock(context.irBuiltIns.unitType, thenStatements)
val irIfNode = JsIrBuilder.buildIfElse(context.irBuiltIns.unitType, irIfCondition, irThenBranch) val irIfNode = JsIrBuilder.buildIfElse(context.irBuiltIns.unitType, irIfCondition, irThenBranch)
statements += irIfNode statements += irIfNode
returnValue = irCacheValue returnValue = irCacheValue()
returnStatements = listOf(cacheVar) returnStatements = listOf(cacheVar)
} else { } else {
statements += bodyStatements statements += bodyStatements
@@ -51,7 +51,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
*/ */
class MultipleCatchesLowering(val context: JsIrBackendContext) : FileLoweringPass { class MultipleCatchesLowering(val context: JsIrBackendContext) : FileLoweringPass {
val litTrue = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true) val litTrue get() = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true)
val unitType = context.irBuiltIns.unitType val unitType = context.irBuiltIns.unitType
val nothingType = context.irBuiltIns.nothingType val nothingType = context.irBuiltIns.nothingType
@@ -70,7 +70,7 @@ class MultipleCatchesLowering(val context: JsIrBackendContext) : FileLoweringPas
val commonType = mergeTypes(aTry.catches.map { it.catchParameter.type }) val commonType = mergeTypes(aTry.catches.map { it.catchParameter.type })
val pendingExceptionDeclaration = JsIrBuilder.buildVar(commonType, data, "\$p") val pendingExceptionDeclaration = JsIrBuilder.buildVar(commonType, data, "\$p")
val pendingException = JsIrBuilder.buildGetValue(pendingExceptionDeclaration.symbol) val pendingException = { JsIrBuilder.buildGetValue(pendingExceptionDeclaration.symbol) }
val branches = mutableListOf<IrBranch>() val branches = mutableListOf<IrBranch>()
@@ -81,9 +81,9 @@ class MultipleCatchesLowering(val context: JsIrBackendContext) : FileLoweringPas
val typeSymbol = type.classifierOrNull val typeSymbol = type.classifierOrNull
val castedPendingException = { val castedPendingException = {
if (type !is IrDynamicType) if (type !is IrDynamicType)
buildImplicitCast(pendingException, type, typeSymbol!!) buildImplicitCast(pendingException(), type, typeSymbol!!)
else else
pendingException pendingException()
} }
val catchBody = catch.result.transform(object : IrElementTransformer<IrValueSymbol> { val catchBody = catch.result.transform(object : IrElementTransformer<IrValueSymbol> {
@@ -98,14 +98,14 @@ class MultipleCatchesLowering(val context: JsIrBackendContext) : FileLoweringPas
branches += IrElseBranchImpl(catch.startOffset, catch.endOffset, litTrue, catchBody) branches += IrElseBranchImpl(catch.startOffset, catch.endOffset, litTrue, catchBody)
break break
} else { } else {
val typeCheck = buildIsCheck(pendingException, type, typeSymbol!!) val typeCheck = buildIsCheck(pendingException(), type, typeSymbol!!)
branches += IrBranchImpl(catch.startOffset, catch.endOffset, typeCheck, catchBody) branches += IrBranchImpl(catch.startOffset, catch.endOffset, typeCheck, catchBody)
} }
} }
if (commonType !is IrDynamicType) { if (commonType !is IrDynamicType) {
val throwStatement = JsIrBuilder.buildThrow(nothingType, pendingException) val throwStatement = JsIrBuilder.buildThrow(nothingType, pendingException())
branches += IrElseBranchImpl(litTrue, JsIrBuilder.buildBlock(nothingType, listOf(throwStatement))) branches += IrElseBranchImpl(litTrue, JsIrBuilder.buildBlock(nothingType, listOf(throwStatement)))
} }
@@ -40,9 +40,9 @@ class ThrowableSuccessorsLowering(context: JsIrBackendContext) : FileLoweringPas
private val propertySetter = context.intrinsics.jsSetJSField.symbol private val propertySetter = context.intrinsics.jsSetJSField.symbol
private val eqeqeqSymbol = context.irBuiltIns.eqeqSymbol private val eqeqeqSymbol = context.irBuiltIns.eqeqSymbol
private val messageName = JsIrBuilder.buildString(stringType, "message") private val messageName get() = JsIrBuilder.buildString(stringType, "message")
private val causeName = JsIrBuilder.buildString(stringType, "cause") private val causeName get() = JsIrBuilder.buildString(stringType, "cause")
private val nameName = JsIrBuilder.buildString(stringType, "name") private val nameName get() = JsIrBuilder.buildString(stringType, "name")
private val throwableClass = context.symbolTable.referenceClass( private val throwableClass = context.symbolTable.referenceClass(
context.getClass(JsIrBackendContext.KOTLIN_PACKAGE_FQN.child(Name.identifier("Throwable"))) context.getClass(JsIrBackendContext.KOTLIN_PACKAGE_FQN.child(Name.identifier("Throwable")))
@@ -121,15 +121,15 @@ class ThrowableSuccessorsLowering(context: JsIrBackendContext) : FileLoweringPas
} }
private fun extractConstructorParameters(expression: IrFunctionAccessExpression): Pair<IrExpression, IrExpression> { private fun extractConstructorParameters(expression: IrFunctionAccessExpression): Pair<IrExpression, IrExpression> {
val nullValue = IrConstImpl.constNull(expression.startOffset, expression.endOffset, nothingNType) val nullValue = { IrConstImpl.constNull(expression.startOffset, expression.endOffset, nothingNType) }
return when { return when {
expression.valueArgumentsCount == 0 -> Pair(nullValue, nullValue) expression.valueArgumentsCount == 0 -> Pair(nullValue(), nullValue())
expression.valueArgumentsCount == 2 -> expression.run { Pair(getValueArgument(0)!!, getValueArgument(1)!!) } expression.valueArgumentsCount == 2 -> expression.run { Pair(getValueArgument(0)!!, getValueArgument(1)!!) }
else -> { else -> {
val arg = expression.getValueArgument(0)!! val arg = expression.getValueArgument(0)!!
when { when {
arg.type.makeNotNull().isThrowable() -> Pair(nullValue, arg) arg.type.makeNotNull().isThrowable() -> Pair(nullValue(), arg)
else -> Pair(arg, nullValue) else -> Pair(arg, nullValue())
} }
} }
} }
@@ -229,7 +229,7 @@ class ThrowableSuccessorsLowering(context: JsIrBackendContext) : FileLoweringPas
} }
val klass = successor.klass val klass = successor.klass
val receiver = IrGetValueImpl(expression.startOffset, expression.endOffset, klass.thisReceiver!!.symbol) val receiver = { IrGetValueImpl(expression.startOffset, expression.endOffset, klass.thisReceiver!!.symbol) }
val fillStatements = fillThrowableInstance(expression, receiver, messageArg, causeArg) val fillStatements = fillThrowableInstance(expression, receiver, messageArg, causeArg)
@@ -240,21 +240,21 @@ class ThrowableSuccessorsLowering(context: JsIrBackendContext) : FileLoweringPas
private fun fillThrowableInstance( private fun fillThrowableInstance(
expression: IrFunctionAccessExpression, expression: IrFunctionAccessExpression,
receiver: IrExpression, receiver: () -> IrExpression,
messageArg: IrExpression, messageArg: IrExpression,
causeArg: IrExpression causeArg: IrExpression
): List<IrStatement> { ): List<IrStatement> {
val setMessage = expression.run { val setMessage = expression.run {
IrSetFieldImpl(startOffset, endOffset, successor.message.symbol, receiver, messageArg, unitType) IrSetFieldImpl(startOffset, endOffset, successor.message.symbol, receiver(), messageArg, unitType)
} }
val setCause = expression.run { val setCause = expression.run {
IrSetFieldImpl(startOffset, endOffset, successor.cause.symbol, receiver, causeArg, unitType) IrSetFieldImpl(startOffset, endOffset, successor.cause.symbol, receiver(), causeArg, unitType)
} }
val setStackTrace = IrCallImpl(expression.startOffset, expression.endOffset, unitType, captureStackFunction).apply { val setStackTrace = IrCallImpl(expression.startOffset, expression.endOffset, unitType, captureStackFunction).apply {
putValueArgument(0, receiver) putValueArgument(0, receiver())
} }
return listOf(setMessage, setCause, setStackTrace) return listOf(setMessage, setCause, setStackTrace)
@@ -264,10 +264,10 @@ class ThrowableSuccessorsLowering(context: JsIrBackendContext) : FileLoweringPas
expression: IrFunctionAccessExpression, expression: IrFunctionAccessExpression,
parent: IrDeclarationParent parent: IrDeclarationParent
): Triple<IrExpression, IrExpression, List<IrStatement>> { ): Triple<IrExpression, IrExpression, List<IrStatement>> {
val nullValue = IrConstImpl.constNull(expression.startOffset, expression.endOffset, nothingNType) val nullValue = { IrConstImpl.constNull(expression.startOffset, expression.endOffset, nothingNType) }
// Wrap parameters into variables to keep original evaluation order // Wrap parameters into variables to keep original evaluation order
return when { return when {
expression.valueArgumentsCount == 0 -> Triple(nullValue, nullValue, emptyList()) expression.valueArgumentsCount == 0 -> Triple(nullValue(), nullValue(), emptyList())
expression.valueArgumentsCount == 2 -> { expression.valueArgumentsCount == 2 -> {
val msg = expression.getValueArgument(0)!! val msg = expression.getValueArgument(0)!!
val cus = expression.getValueArgument(1)!! val cus = expression.getValueArgument(1)!!
@@ -276,7 +276,7 @@ class ThrowableSuccessorsLowering(context: JsIrBackendContext) : FileLoweringPas
val check = JsIrBuilder.buildCall(eqeqeqSymbol, booleanType).apply { val check = JsIrBuilder.buildCall(eqeqeqSymbol, booleanType).apply {
putValueArgument(0, JsIrBuilder.buildGetValue(irValM.symbol)) putValueArgument(0, JsIrBuilder.buildGetValue(irValM.symbol))
putValueArgument(1, nullValue) putValueArgument(1, nullValue())
} }
val msgElvis = JsIrBuilder.buildIfElse( val msgElvis = JsIrBuilder.buildIfElse(
@@ -292,7 +292,7 @@ class ThrowableSuccessorsLowering(context: JsIrBackendContext) : FileLoweringPas
val argValue = JsIrBuilder.buildGetValue(irVal.symbol) val argValue = JsIrBuilder.buildGetValue(irVal.symbol)
when { when {
arg.type.makeNotNull().isThrowable() -> Triple(safeCallToString(irVal), argValue, listOf(irVal)) arg.type.makeNotNull().isThrowable() -> Triple(safeCallToString(irVal), argValue, listOf(irVal))
else -> Triple(argValue, nullValue, listOf(irVal)) else -> Triple(argValue, nullValue(), listOf(irVal))
} }
} }
} }
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.ir.declarations.IrDeclarationParent
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrExpressionWithCopy
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
@@ -31,13 +32,13 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass { class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
private val unit = context.irBuiltIns.unitType private val unit = context.irBuiltIns.unitType
private val unitValue = JsIrBuilder.buildGetObjectValue(unit, unit.classifierOrFail as IrClassSymbol) private val unitValue get() = JsIrBuilder.buildGetObjectValue(unit, unit.classifierOrFail as IrClassSymbol)
private val lit24 = JsIrBuilder.buildInt(context.irBuiltIns.intType, 24) private val lit24 get() = JsIrBuilder.buildInt(context.irBuiltIns.intType, 24)
private val lit16 = JsIrBuilder.buildInt(context.irBuiltIns.intType, 16) private val lit16 get() = JsIrBuilder.buildInt(context.irBuiltIns.intType, 16)
private val byteMask = JsIrBuilder.buildInt(context.irBuiltIns.intType, 0xFF) private val byteMask get() = JsIrBuilder.buildInt(context.irBuiltIns.intType, 0xFF)
private val shortMask = JsIrBuilder.buildInt(context.irBuiltIns.intType, 0xFFFF) private val shortMask get() = JsIrBuilder.buildInt(context.irBuiltIns.intType, 0xFFFF)
private val calculator = JsIrArithBuilder(context) private val calculator = JsIrArithBuilder(context)
@@ -56,14 +57,14 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
private val typeOfIntrinsicSymbol = context.intrinsics.jsTypeOf.symbol private val typeOfIntrinsicSymbol = context.intrinsics.jsTypeOf.symbol
private val jsClassIntrinsicSymbol = context.intrinsics.jsClass private val jsClassIntrinsicSymbol = context.intrinsics.jsClass
private val stringMarker = JsIrBuilder.buildString(context.irBuiltIns.stringType, "string") private val stringMarker get() = JsIrBuilder.buildString(context.irBuiltIns.stringType, "string")
private val booleanMarker = JsIrBuilder.buildString(context.irBuiltIns.stringType, "boolean") private val booleanMarker get() = JsIrBuilder.buildString(context.irBuiltIns.stringType, "boolean")
private val functionMarker = JsIrBuilder.buildString(context.irBuiltIns.stringType, "function") private val functionMarker get() = JsIrBuilder.buildString(context.irBuiltIns.stringType, "function")
private val numberMarker = JsIrBuilder.buildString(context.irBuiltIns.stringType, "number") private val numberMarker get() = JsIrBuilder.buildString(context.irBuiltIns.stringType, "number")
private val litTrue: IrExpression = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true) private val litTrue: IrExpression get() = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true)
private val litFalse: IrExpression = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, false) private val litFalse: IrExpression get() = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, false)
private val litNull: IrExpression = JsIrBuilder.buildNull(context.irBuiltIns.nothingNType) private val litNull: IrExpression get() = JsIrBuilder.buildNull(context.irBuiltIns.nothingNType)
override fun lower(irFile: IrFile) { override fun lower(irFile: IrFile) {
irFile.transformChildren(object : IrElementTransformer<IrDeclarationParent> { irFile.transformChildren(object : IrElementTransformer<IrDeclarationParent> {
@@ -96,9 +97,9 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
val newStatements = mutableListOf<IrStatement>() val newStatements = mutableListOf<IrStatement>()
val argument = cacheValue(expression.argument, newStatements, declaration) val argument = cacheValue(expression.argument, newStatements, declaration)
val irNullCheck = nullCheck(argument) val irNullCheck = nullCheck(argument())
newStatements += JsIrBuilder.buildIfElse(expression.typeOperand, irNullCheck, JsIrBuilder.buildCall(throwNPE), argument) newStatements += JsIrBuilder.buildIfElse(expression.typeOperand, irNullCheck, JsIrBuilder.buildCall(throwNPE), argument())
return expression.run { IrCompositeImpl(startOffset, endOffset, typeOperand, null, newStatements) } return expression.run { IrCompositeImpl(startOffset, endOffset, typeOperand, null, newStatements) }
} }
@@ -117,12 +118,13 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
val newStatements = mutableListOf<IrStatement>() val newStatements = mutableListOf<IrStatement>()
val argument = cacheValue(expression.argument, newStatements, declaration) val argument = cacheValue(expression.argument, newStatements, declaration)
val check = generateTypeCheck(argument, toType) val check = generateTypeCheck(argument, toType)
newStatements += JsIrBuilder.buildIfElse(toType, check, argument, failResult) newStatements += JsIrBuilder.buildIfElse(toType, check, argument(), failResult)
return expression.run { IrCompositeImpl(startOffset, endOffset, toType, null, newStatements) } return expression.run {
IrCompositeImpl(startOffset, endOffset, toType, null, newStatements)
}
} }
private fun lowerImplicitCast(expression: IrTypeOperatorCall) = expression.run { private fun lowerImplicitCast(expression: IrTypeOperatorCall) = expression.run {
@@ -155,18 +157,19 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
assert((expression.operator == IrTypeOperator.NOT_INSTANCEOF) == inverted) assert((expression.operator == IrTypeOperator.NOT_INSTANCEOF) == inverted)
val toType = expression.typeOperand val toType = expression.typeOperand
val isCopyRequired = expression.argument.type.isNullable() && advancedCheckRequired(toType.makeNotNull())
val newStatements = mutableListOf<IrStatement>() val newStatements = mutableListOf<IrStatement>()
val argument = val argument = cacheValue(expression.argument, newStatements, declaration)
if (isCopyRequired) cacheValue(expression.argument, newStatements, declaration) else expression.argument
val check = generateTypeCheck(argument, toType) val check = generateTypeCheck(argument, toType)
val result = if (inverted) calculator.not(check) else check val result = if (inverted) calculator.not(check) else check
newStatements += result
return if (isCopyRequired) { return IrCompositeImpl(
newStatements += result expression.startOffset,
IrCompositeImpl(expression.startOffset, expression.endOffset, toType, null, newStatements) expression.endOffset,
} else result context.irBuiltIns.booleanType,
null,
newStatements
)
} }
private fun nullCheck(value: IrExpression) = JsIrBuilder.buildCall(eqeq).apply { private fun nullCheck(value: IrExpression) = JsIrBuilder.buildCall(eqeq).apply {
@@ -178,40 +181,41 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
value: IrExpression, value: IrExpression,
newStatements: MutableList<IrStatement>, newStatements: MutableList<IrStatement>,
declaration: IrDeclarationParent declaration: IrDeclarationParent
): IrExpression { ): () -> IrExpressionWithCopy {
val varDeclaration = JsIrBuilder.buildVar(value.type, declaration, initializer = value) val varDeclaration = JsIrBuilder.buildVar(value.type, declaration, initializer = value)
newStatements += varDeclaration newStatements += varDeclaration
return JsIrBuilder.buildGetValue(varDeclaration.symbol) return { JsIrBuilder.buildGetValue(varDeclaration.symbol) }
} }
private fun generateTypeCheck(argument: IrExpression, toType: IrType): IrExpression { private fun generateTypeCheck(argument: () -> IrExpressionWithCopy, toType: IrType): IrExpression {
// TODO: Fix unbound symbols (in inline) // TODO: Fix unbound symbols (in inline)
toType.classifierOrNull?.apply { toType.classifierOrNull?.apply {
if (!isBound) { if (!isBound) {
return argument return argument()
} }
} }
val toNotNullable = toType.makeNotNull() val toNotNullable = toType.makeNotNull()
val instanceCheck = generateTypeCheckNonNull(argument, toNotNullable) val argumentInstance = argument()
val isFromNullable = argument.type.isNullable() val instanceCheck = generateTypeCheckNonNull(argumentInstance, toNotNullable)
val isFromNullable = argumentInstance.type.isNullable()
val isToNullable = toType.isNullable() val isToNullable = toType.isNullable()
val isNativeCheck = !advancedCheckRequired(toNotNullable) val isNativeCheck = !advancedCheckRequired(toNotNullable)
return when { return when {
!isFromNullable -> instanceCheck // ! -> * !isFromNullable -> instanceCheck // ! -> *
isToNullable -> calculator.run { oror(nullCheck(argument), instanceCheck) } // * -> ? isToNullable -> calculator.run { oror(nullCheck(argument()), instanceCheck) } // * -> ?
else -> if (isNativeCheck) instanceCheck else calculator.run { else -> if (isNativeCheck) instanceCheck else calculator.run {
andand( andand(
not(nullCheck(argument)), not(nullCheck(argument())),
instanceCheck instanceCheck
) )
} // ? -> ! } // ? -> !
} }
} }
private fun generateTypeCheckNonNull(argument: IrExpression, toType: IrType): IrExpression { private fun generateTypeCheckNonNull(argument: IrExpressionWithCopy, toType: IrType): IrExpression {
assert(!toType.isMarkedNullable()) assert(!toType.isMarkedNullable())
return when { return when {
toType.isAny() -> generateIsObjectCheck(argument) toType.isAny() -> generateIsObjectCheck(argument)
@@ -232,7 +236,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
putValueArgument(0, argument) putValueArgument(0, argument)
} }
private fun generateTypeCheckWithTypeParameter(argument: IrExpression, toType: IrType): IrExpression { private fun generateTypeCheckWithTypeParameter(argument: IrExpressionWithCopy, toType: IrType): IrExpression {
val typeParameterSymbol = val typeParameterSymbol =
(toType.classifierOrNull as? IrTypeParameterSymbol) ?: error("expected type parameter, but $toType") (toType.classifierOrNull as? IrTypeParameterSymbol) ?: error("expected type parameter, but $toType")
@@ -244,9 +248,8 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
val typeParameter = typeParameterSymbol.owner val typeParameter = typeParameterSymbol.owner
assert(!typeParameter.isReified) { "reified parameters have to be lowered before" } assert(!typeParameter.isReified) { "reified parameters have to be lowered before" }
return typeParameter.superTypes.fold(litTrue) { r, t -> return typeParameter.superTypes.fold(litTrue) { r, t ->
val check = generateTypeCheckNonNull(argument, t.makeNotNull()) val check = generateTypeCheckNonNull(argument.copy(), t.makeNotNull())
calculator.and(r, check) calculator.and(r, check)
} }
} }
@@ -314,25 +317,23 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
val isNullable = expression.argument.type.isNullable() val isNullable = expression.argument.type.isNullable()
val toType = expression.typeOperand val toType = expression.typeOperand
fun maskOp(arg: IrExpression, mask: IrExpression, shift: IrExpression) = calculator.run { fun maskOp(arg: IrExpression, mask: IrExpression, shift: IrExpressionWithCopy) = calculator.run {
shr(shl(and(arg, mask), shift), shift) shr(shl(and(arg, mask), shift), shift.copy())
} }
val newStatements = mutableListOf<IrStatement>() val newStatements = mutableListOf<IrStatement>()
val argument = cacheValue(expression.argument, newStatements, declaration)
val argument =
if (isNullable) cacheValue(expression.argument, newStatements, declaration) else expression.argument
val casted = when { val casted = when {
toType.isByte() -> maskOp(argument, byteMask, lit24) toType.isByte() -> maskOp(argument(), byteMask, lit24)
toType.isShort() -> maskOp(argument, shortMask, lit16) toType.isShort() -> maskOp(argument(), shortMask, lit16)
toType.isLong() -> JsIrBuilder.buildCall(context.intrinsics.jsToLong).apply { toType.isLong() -> JsIrBuilder.buildCall(context.intrinsics.jsToLong).apply {
putValueArgument(0, argument) putValueArgument(0, argument())
} }
else -> error("Unreachable execution (coercion to non-Integer type") else -> error("Unreachable execution (coercion to non-Integer type")
} }
newStatements += if (isNullable) JsIrBuilder.buildIfElse(toType, nullCheck(argument), litNull, casted) else casted newStatements += if (isNullable) JsIrBuilder.buildIfElse(toType, nullCheck(argument()), litNull, casted) else casted
return expression.run { IrCompositeImpl(startOffset, endOffset, toType, null, newStatements) } return expression.run { IrCompositeImpl(startOffset, endOffset, toType, null, newStatements) }
} }
@@ -943,7 +943,7 @@ internal class SuspendFunctionsLowering(val context: JsIrBackendContext): FileLo
} }
} }
function.transform(LiveLocalsTransformer(localToPropertyMap, JsIrBuilder.buildGetValue(thisReceiver), unit), null) function.transform(LiveLocalsTransformer(localToPropertyMap, { JsIrBuilder.buildGetValue(thisReceiver) } , unit), null)
} }
private fun computeLivenessAtSuspensionPoints(body: IrBody): Map<IrCall, List<IrValueDeclaration>> { private fun computeLivenessAtSuspensionPoints(body: IrBody): Map<IrCall, List<IrValueDeclaration>> {
@@ -147,19 +147,19 @@ class SuspendedTerminatorsCollector(suspendableNodes: MutableSet<IrElement>) : S
class LiveLocalsTransformer( class LiveLocalsTransformer(
private val localMap: Map<IrValueSymbol, IrFieldSymbol>, private val localMap: Map<IrValueSymbol, IrFieldSymbol>,
private val receiver: IrExpression, private val receiver: () -> IrExpression,
private val unitType: IrType private val unitType: IrType
) : ) :
IrElementTransformerVoid() { IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression { override fun visitGetValue(expression: IrGetValue): IrExpression {
val field = localMap[expression.symbol] ?: return expression val field = localMap[expression.symbol] ?: return expression
return expression.run { IrGetFieldImpl(startOffset, endOffset, field, type, receiver, origin) } return expression.run { IrGetFieldImpl(startOffset, endOffset, field, type, receiver(), origin) }
} }
override fun visitSetVariable(expression: IrSetVariable): IrExpression { override fun visitSetVariable(expression: IrSetVariable): IrExpression {
expression.transformChildrenVoid(this) expression.transformChildrenVoid(this)
val field = localMap[expression.symbol] ?: return expression val field = localMap[expression.symbol] ?: return expression
return expression.run { IrSetFieldImpl(startOffset, endOffset, field, receiver, value, unitType, origin) } return expression.run { IrSetFieldImpl(startOffset, endOffset, field, receiver(), value, unitType, origin) }
} }
override fun visitVariable(declaration: IrVariable): IrStatement { override fun visitVariable(declaration: IrVariable): IrStatement {
@@ -167,7 +167,7 @@ class LiveLocalsTransformer(
val field = localMap[declaration.symbol] ?: return declaration val field = localMap[declaration.symbol] ?: return declaration
val initializer = declaration.initializer val initializer = declaration.initializer
return if (initializer != null) { return if (initializer != null) {
declaration.run { IrSetFieldImpl(startOffset, endOffset, field, receiver, initializer, unitType) } declaration.run { IrSetFieldImpl(startOffset, endOffset, field, receiver(), initializer, unitType) }
} else { } else {
JsIrBuilder.buildComposite(declaration.type) JsIrBuilder.buildComposite(declaration.type)
} }
@@ -96,7 +96,7 @@ private class ReturnableBlockTransformer(
return super.visitDeclaration(declaration, data) return super.visitDeclaration(declaration, data)
} }
private val constFalse = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, false) private val constFalse get() = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, false)
override fun visitContainerExpression(expression: IrContainerExpression, data: ReturnableBlockLoweringContext): IrExpression { override fun visitContainerExpression(expression: IrContainerExpression, data: ReturnableBlockLoweringContext): IrExpression {
if (expression !is IrReturnableBlock) return super.visitContainerExpression(expression, data) if (expression !is IrReturnableBlock) return super.visitContainerExpression(expression, data)
@@ -28,6 +28,6 @@ interface IrExpression : IrStatement, IrVarargElement {
} }
interface IrExpressionWithCopy : IrExpression { interface IrExpressionWithCopy : IrExpression {
fun copy(): IrExpression fun copy(): IrExpressionWithCopy
} }