Optimize more redundant kotlin/jvm/internal/Refs
The number of initializations of the `value` field before the live range begins does not really matter so long as we insert a write of a default value to the local if there were none.
This commit is contained in:
@@ -1081,27 +1081,23 @@ public class AsmUtil {
|
||||
}
|
||||
|
||||
public static void pushDefaultValueOnStack(@NotNull Type type, @NotNull InstructionAdapter v) {
|
||||
if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
|
||||
v.aconst(null);
|
||||
}
|
||||
else {
|
||||
pushDefaultPrimitiveValueOnStack(type, v);
|
||||
}
|
||||
v.visitInsn(defaultValueOpcode(type));
|
||||
}
|
||||
|
||||
public static void pushDefaultPrimitiveValueOnStack(@NotNull Type type, @NotNull InstructionAdapter v) {
|
||||
public static int defaultValueOpcode(@NotNull Type type) {
|
||||
if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
|
||||
return ACONST_NULL;
|
||||
}
|
||||
if (type.getSort() == Type.FLOAT) {
|
||||
v.fconst(0);
|
||||
return FCONST_0;
|
||||
}
|
||||
else if (type.getSort() == Type.DOUBLE) {
|
||||
v.dconst(0);
|
||||
if (type.getSort() == Type.DOUBLE) {
|
||||
return DCONST_0;
|
||||
}
|
||||
else if (type.getSort() == Type.LONG) {
|
||||
v.lconst(0);
|
||||
}
|
||||
else {
|
||||
v.iconst(0);
|
||||
if (type.getSort() == Type.LONG) {
|
||||
return LCONST_0;
|
||||
}
|
||||
return ICONST_0;
|
||||
}
|
||||
|
||||
public static boolean isInstancePropertyWithStaticBackingField(@NotNull PropertyDescriptor propertyDescriptor) {
|
||||
|
||||
@@ -636,11 +636,8 @@ public abstract class StackValue {
|
||||
if (toType.equals(UNIT_TYPE) || toType.equals(OBJECT_TYPE)) {
|
||||
putUnitInstance(v);
|
||||
}
|
||||
else if (toType.getSort() == Type.OBJECT || toType.getSort() == Type.ARRAY) {
|
||||
v.aconst(null);
|
||||
}
|
||||
else {
|
||||
pushDefaultPrimitiveValueOnStack(toType, v);
|
||||
pushDefaultValueOnStack(toType, v);
|
||||
}
|
||||
}
|
||||
else if (toType.equals(UNIT_TYPE)) {
|
||||
|
||||
+22
-26
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.codegen.optimization
|
||||
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
@@ -207,13 +208,6 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
|
||||
refValue.localVarIndex = localVar.index
|
||||
}
|
||||
|
||||
val startIndex = localVar.start.getIndex()
|
||||
val initFieldInsns = refValue.putFieldInsns.filter { it.getIndex() < startIndex }
|
||||
if (initFieldInsns.size != 1) {
|
||||
refValue.hazard = true
|
||||
continue
|
||||
}
|
||||
|
||||
val cleanInstructions = findCleanInstructions(refValue, oldVarIndex, methodNode.instructions)
|
||||
if (cleanInstructions.size > 1) {
|
||||
refValue.hazard = true
|
||||
@@ -227,14 +221,14 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
|
||||
return InsnSequence(instructions).filterIsInstance<VarInsnNode>().filter {
|
||||
it.opcode == Opcodes.ASTORE && it.`var` == oldVarIndex
|
||||
}.filter {
|
||||
it.previous?.opcode == Opcodes.ACONST_NULL
|
||||
}.filter {
|
||||
val operationIndex = instructions.indexOf(it)
|
||||
val localVariableNode = refValue.localVar!!
|
||||
instructions.indexOf(localVariableNode.start) < operationIndex && operationIndex < instructions.indexOf(
|
||||
localVariableNode.end
|
||||
)
|
||||
}.toList()
|
||||
it.previous?.opcode == Opcodes.ACONST_NULL
|
||||
}.filter {
|
||||
val operationIndex = instructions.indexOf(it)
|
||||
val localVariableNode = refValue.localVar!!
|
||||
instructions.indexOf(localVariableNode.start) < operationIndex && operationIndex < instructions.indexOf(
|
||||
localVariableNode.end
|
||||
)
|
||||
}.toList()
|
||||
}
|
||||
|
||||
private fun rewrite() {
|
||||
@@ -250,9 +244,17 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
|
||||
|
||||
private fun rewriteRefValue(capturedVar: CapturedVarDescriptor) {
|
||||
methodNode.instructions.run {
|
||||
capturedVar.localVar!!.let {
|
||||
it.signature = null
|
||||
it.desc = capturedVar.valueType.descriptor
|
||||
val localVar = capturedVar.localVar!!
|
||||
localVar.signature = null
|
||||
localVar.desc = capturedVar.valueType.descriptor
|
||||
|
||||
val loadOpcode = capturedVar.valueType.getOpcode(Opcodes.ILOAD)
|
||||
val storeOpcode = capturedVar.valueType.getOpcode(Opcodes.ISTORE)
|
||||
|
||||
if (capturedVar.putFieldInsns.none { it.getIndex() < localVar.start.getIndex() }) {
|
||||
// variable needs to be initialized before its live range can begin
|
||||
insertBefore(capturedVar.newInsn, InsnNode(AsmUtil.defaultValueOpcode(capturedVar.valueType)))
|
||||
insertBefore(capturedVar.newInsn, VarInsnNode(storeOpcode, capturedVar.localVarIndex))
|
||||
}
|
||||
|
||||
remove(capturedVar.newInsn)
|
||||
@@ -260,14 +262,8 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
|
||||
capturedVar.stackInsns.forEach { remove(it) }
|
||||
capturedVar.aloadInsns.forEach { remove(it) }
|
||||
capturedVar.astoreInsns.forEach { remove(it) }
|
||||
|
||||
capturedVar.getFieldInsns.forEach {
|
||||
set(it, VarInsnNode(capturedVar.valueType.getOpcode(Opcodes.ILOAD), capturedVar.localVarIndex))
|
||||
}
|
||||
|
||||
capturedVar.putFieldInsns.forEach {
|
||||
set(it, VarInsnNode(capturedVar.valueType.getOpcode(Opcodes.ISTORE), capturedVar.localVarIndex))
|
||||
}
|
||||
capturedVar.getFieldInsns.forEach { set(it, VarInsnNode(loadOpcode, capturedVar.localVarIndex)) }
|
||||
capturedVar.putFieldInsns.forEach { set(it, VarInsnNode(storeOpcode, capturedVar.localVarIndex)) }
|
||||
|
||||
//after visiting block codegen tries to delete all allocated references:
|
||||
// see ExpressionCodegen.addLeaveTaskToRemoveLocalVariableFromFrameMap
|
||||
|
||||
Vendored
-3
@@ -1,6 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// TODO KT-36648 Captured variables not optimized in JVM_IR
|
||||
|
||||
fun test() {
|
||||
var x = 0
|
||||
run {
|
||||
|
||||
-3
@@ -1,6 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// TODO KT-36648 Captured variables not optimized in JVM_IR
|
||||
|
||||
fun test() {
|
||||
var x = 0
|
||||
run { ++x }
|
||||
|
||||
-7
@@ -1,11 +1,4 @@
|
||||
// WITH_RUNTIME
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// TODO KT-36648 Captured variables not optimized in JVM_IR
|
||||
|
||||
// In JVM IR, SharedVariablesLowering transforms `x` into a shared variable to be able to update it from a lambda,
|
||||
// which is a separate function (...$lambda-0).
|
||||
// If we keep the existing representation of lambda bodies as separate functions in JVM IR, the only viable option to fix this test
|
||||
// seems to support this case in the bytecode optimization pass CapturedVarsOptimizationMethodTransformer.
|
||||
|
||||
fun box(): String {
|
||||
val x: String
|
||||
|
||||
-3
@@ -1,6 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// TODO KT-36648 Captured variables not optimized in JVM_IR
|
||||
|
||||
fun box(): String {
|
||||
var xl = 0L // Long, size 2
|
||||
var xi = 0 // Int, size 1
|
||||
|
||||
-3
@@ -1,6 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// TODO KT-36648 Captured variables not optimized in JVM_IR
|
||||
|
||||
fun add(x: Int, y: Int) = x + y
|
||||
|
||||
fun test() {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// TODO KT-36648 Captured variables not optimized in JVM_IR
|
||||
|
||||
fun test(): java.lang.Integer {
|
||||
val c: java.lang.Integer
|
||||
run {
|
||||
@@ -10,4 +7,9 @@ fun test(): java.lang.Integer {
|
||||
}
|
||||
|
||||
// 2 ASTORE 0
|
||||
// 1 LOCALVARIABLE c Ljava/lang/Integer; L1 L.* 0
|
||||
|
||||
// JVM_TEMPLATES
|
||||
// 1 LOCALVARIABLE c Ljava/lang/Integer;
|
||||
|
||||
// JVM_IR_TEMPLATES
|
||||
// 1 LOCALVARIABLE c Ljava/lang/Object;
|
||||
@@ -6,5 +6,5 @@ fun test(): java.lang.Integer {
|
||||
return c
|
||||
}
|
||||
|
||||
// 1 ASTORE 0
|
||||
// 1 LOCALVARIABLE c Lkotlin/jvm/internal/Ref\$ObjectRef; L1 L.* 0
|
||||
// 2 ASTORE 0
|
||||
// 1 LOCALVARIABLE c Ljava/lang/Object;
|
||||
@@ -1,5 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
|
||||
inline fun <reified T> foo(default: T): T {
|
||||
val t: T
|
||||
run {
|
||||
@@ -13,6 +11,6 @@ fun test() {
|
||||
}
|
||||
|
||||
// two in foo and two in test
|
||||
|
||||
// 4 ASTORE 2
|
||||
// 1 LOCALVARIABLE t\$iv Ljava/lang/Object; L3 L.* 2
|
||||
// 1 LOCALVARIABLE t Ljava/lang/Object;
|
||||
// 1 LOCALVARIABLE t\$iv Ljava/lang/Object;
|
||||
+3
-4
@@ -1,4 +1,3 @@
|
||||
|
||||
inline fun <reified T> foo(default: T): T {
|
||||
var t: T
|
||||
run {
|
||||
@@ -12,6 +11,6 @@ fun test() {
|
||||
}
|
||||
|
||||
// two in foo and two in test
|
||||
|
||||
// 2 ASTORE 2
|
||||
// 1 LOCALVARIABLE t\$iv Lkotlin/jvm/internal/Ref\$ObjectRef; L3 L.* 2
|
||||
// 4 ASTORE 2
|
||||
// 1 LOCALVARIABLE t Ljava/lang/Object;
|
||||
// 1 LOCALVARIABLE t\$iv Ljava/lang/Object;
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// TODO KT-36648 Captured variables not optimized in JVM_IR
|
||||
// TODO: JVM_IR uses ObjectRef instead of IntRef for the value
|
||||
|
||||
fun test(): UInt {
|
||||
val c: UInt
|
||||
@@ -10,4 +10,4 @@ fun test(): UInt {
|
||||
}
|
||||
|
||||
// 2 ISTORE 0
|
||||
// 1 LOCALVARIABLE c I L1 L.* 0
|
||||
// 1 LOCALVARIABLE c I
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// TODO KT-36648 Captured variables not optimized in JVM_IR
|
||||
// TODO: JVM_IR uses ObjectRef instead of IntRef for the value
|
||||
|
||||
fun test(): UInt {
|
||||
var c: UInt
|
||||
@@ -9,5 +9,5 @@ fun test(): UInt {
|
||||
return c
|
||||
}
|
||||
|
||||
// 1 ASTORE 0
|
||||
// 1 LOCALVARIABLE c Lkotlin/jvm/internal/Ref\$IntRef; L1 L.* 0
|
||||
// 2 ISTORE 0
|
||||
// 1 LOCALVARIABLE c I
|
||||
@@ -1,6 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// TODO KT-36648 Captured variables not optimized in JVM_IR
|
||||
|
||||
fun test(): Char {
|
||||
lateinit var c: Any
|
||||
run {
|
||||
@@ -9,5 +6,11 @@ fun test(): Char {
|
||||
return c as Char
|
||||
}
|
||||
|
||||
// 1 LOCALVARIABLE c Ljava/lang/Object;
|
||||
|
||||
// JVM_TEMPLATES
|
||||
// 2 ASTORE 0
|
||||
// 1 LOCALVARIABLE c Ljava/lang/Object; L1 L.* 0
|
||||
|
||||
// JVM_IR_TEMPLATES
|
||||
// 3 ASTORE 0
|
||||
// *two* of them are after the start of c's live range
|
||||
@@ -1,6 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// TODO KT-36648 Captured variables not optimized in JVM_IR
|
||||
|
||||
fun test(): Char {
|
||||
val c: Char
|
||||
run {
|
||||
@@ -12,4 +9,4 @@ fun test(): Char {
|
||||
|
||||
// The first on declaration, the other on initialization
|
||||
// 2 ISTORE 0
|
||||
// 1 LOCALVARIABLE c C L1 L.* 0
|
||||
// 1 LOCALVARIABLE c C
|
||||
@@ -6,5 +6,5 @@ fun test(): Char {
|
||||
return c
|
||||
}
|
||||
|
||||
// 1 ASTORE 0
|
||||
// 1 LOCALVARIABLE c Lkotlin/jvm/internal/Ref\$CharRef; L1 L.* 0
|
||||
// 2 ISTORE 0
|
||||
// 1 LOCALVARIABLE c C
|
||||
-2
@@ -1,5 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// TODO KT-36813 Support code generated by JVM_IR in redundant null check optimization
|
||||
fun almostAlwaysTrue() = true
|
||||
|
||||
fun test() {
|
||||
|
||||
-2
@@ -1,5 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// TODO KT-36813 Support code generated by JVM_IR in redundant null check optimization
|
||||
fun test() {
|
||||
lateinit var z: String
|
||||
run {
|
||||
|
||||
Reference in New Issue
Block a user