JVM_IR: Do not add redundant field initializers.

Initializers are "set field" expressions and are considered redundant
when they are:
1. In the primary constructor; and
2. Set the field to `0`, `false`, or `null`; and
3. Have a `null` origin. I.e., not in an initializer block or
constructor body, and therefore the field could not have been set by a
prior expression.
This commit is contained in:
Mark Punzalan
2019-01-30 16:18:59 -08:00
committed by max-kammerer
parent 9e8972f1f9
commit e91a16556c
4 changed files with 149 additions and 1 deletions
@@ -33,6 +33,8 @@ import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.isMarkedNullable
import org.jetbrains.kotlin.ir.types.isNothing
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.dump
@@ -455,12 +457,54 @@ class ExpressionCodegen(
}
override fun visitSetField(expression: IrSetField, data: BlockInfo): StackValue {
val expressionValue = expression.value
if (irFunction is IrConstructor && irFunction.isPrimary) {
// Do not add redundant field initializers that initialize to default values.
// "expression.origin == null" means that the field is initialized when it is declared,
// i.e., not in an initializer block or constructor body.
if (expression.origin == null && expressionValue is IrConst<*> && isDefaultValueForType(
expression.symbol.owner.type, expressionValue
)
) return none()
}
expression.markLineNumber(startOffset = true)
val fieldValue = generateFieldValue(expression, data)
fieldValue.store(expression.value.accept(this, data), mv)
fieldValue.store(expressionValue.accept(this, data), mv)
return none()
}
/**
* Returns true if the given constant value is the JVM's default value for the given type.
* See: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.3
*/
private fun isDefaultValueForType(fieldType: IrType, constExpression: IrConst<*>): Boolean {
val value = constExpression.value
val type = constExpression.asmType
if (isPrimitive(type)) {
if (!fieldType.isMarkedNullable() && value is Number) {
if (type in setOf(Type.INT_TYPE, Type.BYTE_TYPE, Type.LONG_TYPE, Type.SHORT_TYPE) && value.toLong() == 0L) {
return true
}
if (type === Type.DOUBLE_TYPE && value.toDouble().equals(0.0)) {
return true
}
if (type === Type.FLOAT_TYPE && value.toFloat().equals(0.0f)) {
return true
}
}
if (type === Type.BOOLEAN_TYPE && value is Boolean && !value) {
return true
}
if (type === Type.CHAR_TYPE && value is Char && value.toInt() == 0) {
return true
}
} else if (value == null) {
return true
}
return false
}
private fun generateLocal(symbol: IrSymbol, type: Type): StackValue {
val index = findLocalIndex(symbol)
StackValue.local(index, type).put(type, mv)