JVM_IR KT-27427 use checkNotNull with message in checked casts
This would prevent extra branching in 'as <not-null-type>', which usually would not be covered.
This commit is contained in:
+57
-17
@@ -72,6 +72,7 @@ class RedundantNullCheckMethodTransformer(private val generationState: Generatio
|
||||
val value = when {
|
||||
insn.isInstanceOfOrNullCheck() -> frame.top()
|
||||
insn.isCheckNotNull() -> frame.top()
|
||||
insn.isCheckNotNullWithMessage() -> frame.peek(1)
|
||||
insn.isCheckExpressionValueIsNotNull() -> frame.peek(1)
|
||||
else -> null
|
||||
} as? StrictBasicValue ?: continue
|
||||
@@ -88,6 +89,7 @@ class RedundantNullCheckMethodTransformer(private val generationState: Generatio
|
||||
opcode == Opcodes.IFNONNULL ||
|
||||
opcode == Opcodes.INSTANCEOF ||
|
||||
isCheckNotNull() ||
|
||||
isCheckNotNullWithMessage() ||
|
||||
isCheckExpressionValueIsNotNull()
|
||||
|
||||
private fun transformTrivialChecks(nullabilityMap: Map<AbstractInsnNode, StrictBasicValue>) {
|
||||
@@ -99,11 +101,13 @@ class RedundantNullCheckMethodTransformer(private val generationState: Generatio
|
||||
Opcodes.INSTANCEOF -> transformInstanceOf(insn as TypeInsnNode, nullability, value)
|
||||
|
||||
Opcodes.INVOKESTATIC -> {
|
||||
if (insn.isCheckNotNull()) {
|
||||
transformTrivialCheckNotNull(insn, nullability)
|
||||
}
|
||||
if (insn.isCheckExpressionValueIsNotNull()) {
|
||||
transformTrivialCheckExpressionValueIsNotNull(insn, nullability)
|
||||
when {
|
||||
insn.isCheckNotNull() ->
|
||||
transformTrivialCheckNotNull(insn, nullability)
|
||||
insn.isCheckNotNullWithMessage() ->
|
||||
transformTrivialCheckNotNullWithMessage(insn, nullability)
|
||||
insn.isCheckExpressionValueIsNotNull() ->
|
||||
transformTrivialCheckExpressionValueIsNotNull(insn, nullability)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,6 +154,17 @@ class RedundantNullCheckMethodTransformer(private val generationState: Generatio
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformTrivialCheckNotNullWithMessage(insn: AbstractInsnNode, nullability: Nullability) {
|
||||
if (nullability != Nullability.NOT_NULL) return
|
||||
val ldcInsn = insn.previous?.takeIf { it.opcode == Opcodes.LDC } ?: return
|
||||
val previousInsn = ldcInsn.previous?.takeIf { it.opcode == Opcodes.DUP || it.opcode == Opcodes.ALOAD } ?: return
|
||||
methodNode.instructions.run {
|
||||
remove(previousInsn)
|
||||
remove(ldcInsn)
|
||||
remove(insn)
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformTrivialCheckExpressionValueIsNotNull(insn: AbstractInsnNode, nullability: Nullability) {
|
||||
if (nullability != Nullability.NOT_NULL) return
|
||||
val ldcInsn = insn.previous?.takeIf { it.opcode == Opcodes.LDC } ?: return
|
||||
@@ -185,12 +200,14 @@ class RedundantNullCheckMethodTransformer(private val generationState: Generatio
|
||||
}
|
||||
|
||||
insn.isCheckNotNull() -> {
|
||||
val previous = insn.previous ?: continue
|
||||
val aLoadInsn = if (previous.opcode == Opcodes.DUP) {
|
||||
previous.previous ?: continue
|
||||
} else previous
|
||||
if (aLoadInsn.opcode != Opcodes.ALOAD) continue
|
||||
addDependentCheck(insn, aLoadInsn as VarInsnNode)
|
||||
val checkedValueInsn = insn.previous ?: continue
|
||||
addDependentCheckForCheckNotNull(insn, checkedValueInsn)
|
||||
}
|
||||
|
||||
insn.isCheckNotNullWithMessage() -> {
|
||||
val ldcInsn = insn.previous?.takeIf { it.opcode == Opcodes.LDC } ?: continue
|
||||
val checkedValueInsn = ldcInsn.previous ?: continue
|
||||
addDependentCheckForCheckNotNull(insn, checkedValueInsn)
|
||||
}
|
||||
|
||||
insn.isCheckParameterIsNotNull() -> {
|
||||
@@ -221,6 +238,16 @@ class RedundantNullCheckMethodTransformer(private val generationState: Generatio
|
||||
}
|
||||
}
|
||||
|
||||
private fun addDependentCheckForCheckNotNull(insn: AbstractInsnNode, checkedValueInsn: AbstractInsnNode) {
|
||||
val aLoadInsn = if (checkedValueInsn.opcode == Opcodes.DUP) {
|
||||
checkedValueInsn.previous ?: return
|
||||
} else {
|
||||
checkedValueInsn
|
||||
}
|
||||
if (aLoadInsn.opcode != Opcodes.ALOAD) return
|
||||
addDependentCheck(insn, aLoadInsn as VarInsnNode)
|
||||
}
|
||||
|
||||
private fun addDependentCheck(insn: AbstractInsnNode, aLoadInsn: VarInsnNode) {
|
||||
checksDependingOnVariable.getOrPut(aLoadInsn.`var`) {
|
||||
SmartList()
|
||||
@@ -249,7 +276,8 @@ class RedundantNullCheckMethodTransformer(private val generationState: Generatio
|
||||
injectAssumptionsForNullCheck(varIndex, insn as JumpInsnNode)
|
||||
Opcodes.INVOKESTATIC -> {
|
||||
when {
|
||||
insn.isCheckNotNull() || insn.isCheckParameterIsNotNull() || insn.isCheckExpressionValueIsNotNull() ->
|
||||
insn.isCheckNotNull() || insn.isCheckNotNullWithMessage() ||
|
||||
insn.isCheckParameterIsNotNull() || insn.isCheckExpressionValueIsNotNull() ->
|
||||
injectAssumptionsForNotNullAssertion(varIndex, insn)
|
||||
insn.isPseudo(PseudoInsn.STORE_NOT_NULL) ->
|
||||
injectCodeForStoreNotNull(insn)
|
||||
@@ -300,13 +328,16 @@ class RedundantNullCheckMethodTransformer(private val generationState: Generatio
|
||||
}
|
||||
|
||||
private fun NullabilityAssumptions.injectAssumptionsForNotNullAssertion(varIndex: Int, insn: AbstractInsnNode) {
|
||||
// ALOAD v
|
||||
// DUP
|
||||
// INVOKESTATIC checkNotNull
|
||||
// ( INVOKESTATIC checkNotNull
|
||||
// | LDC <message>; INVOKESTATIC checkNotNull(Object, String)V
|
||||
// )
|
||||
// <...> -- v is not null here (otherwise an exception was thrown)
|
||||
|
||||
// ALOAD v
|
||||
// INVOKESTATIC checkNotNull
|
||||
// DUP?
|
||||
// ( INVOKESTATIC checkNotNull
|
||||
// | LDC <message>; INVOKESTATIC checkNotNull(Object, String)V
|
||||
// )
|
||||
// <...> -- v is not null here (otherwise an exception was thrown)
|
||||
|
||||
// ALOAD v
|
||||
@@ -317,7 +348,9 @@ class RedundantNullCheckMethodTransformer(private val generationState: Generatio
|
||||
// ALOAD v
|
||||
// DUP
|
||||
// LDC *
|
||||
// INVOKESTATIC checkExpressionValueIsNotNull/checkNotNullExpressionValue
|
||||
// ( INVOKESTATIC checkExpressionValueIsNotNull
|
||||
// | INVOKESTATIC checkNotNullExpressionValue
|
||||
// )
|
||||
// <...> -- v is not null here (otherwise an exception was thrown)
|
||||
|
||||
methodNode.instructions.insert(insn, listOfSynthetics {
|
||||
@@ -436,6 +469,13 @@ internal fun AbstractInsnNode.isCheckNotNull() =
|
||||
desc == "(Ljava/lang/Object;)V"
|
||||
}
|
||||
|
||||
internal fun AbstractInsnNode.isCheckNotNullWithMessage() =
|
||||
isInsn<MethodInsnNode>(Opcodes.INVOKESTATIC) {
|
||||
owner == IntrinsicMethods.INTRINSICS_CLASS_NAME &&
|
||||
name == "checkNotNull" &&
|
||||
desc == "(Ljava/lang/Object;Ljava/lang/String;)V"
|
||||
}
|
||||
|
||||
fun MethodNode.usesLocalExceptParameterNullCheck(index: Int): Boolean =
|
||||
instructions.toArray().any {
|
||||
it is VarInsnNode && it.opcode == Opcodes.ALOAD && it.`var` == index && !it.isParameterCheckedForNull()
|
||||
|
||||
+2
-1
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.removeUnusedLocalVariables
|
||||
import org.jetbrains.kotlin.codegen.optimization.nullCheck.isCheckExpressionValueIsNotNull
|
||||
import org.jetbrains.kotlin.codegen.optimization.nullCheck.isCheckNotNullWithMessage
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
@@ -288,7 +289,7 @@ class TemporaryVariablesEliminationTransformer(private val state: GenerationStat
|
||||
|
||||
if ((aLoad1Insn as VarInsnNode).`var` == tmp.index &&
|
||||
(ldcInsn as LdcInsnNode).cst is String &&
|
||||
invokeStaticInsn.isCheckExpressionValueIsNotNull() &&
|
||||
(invokeStaticInsn.isCheckExpressionValueIsNotNull() || invokeStaticInsn.isCheckNotNullWithMessage()) &&
|
||||
(aLoad2Insn as VarInsnNode).`var` == tmp.index
|
||||
) {
|
||||
// Replace instruction sequence:
|
||||
|
||||
Reference in New Issue
Block a user