Keep track of array types in OptimizationBasicInterpreter

Merging array types with different element types, for example
`[Lj/l/String;` and `[Lj/l/Object;`, now produces `[Lj/l/Object;`
(instead of `Lj/l/Object;`), which allows for more precise tracking of
null values because we assume that AALOAD on a non-array typed value is
possible only if that value is null.

 #KT-54802 Fixed
This commit is contained in:
Alexander Udalov
2022-11-05 12:12:33 +01:00
parent a79927742c
commit d7a58a7c6c
11 changed files with 105 additions and 20 deletions
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen.optimization.common;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.codegen.AsmUtil;
import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
import org.jetbrains.org.objectweb.asm.Handle;
import org.jetbrains.org.objectweb.asm.Opcodes;
import org.jetbrains.org.objectweb.asm.Type;
@@ -152,13 +153,6 @@ public class OptimizationBasicInterpreter extends Interpreter<BasicValue> implem
@NotNull BasicValue value1,
@NotNull BasicValue value2
) throws AnalyzerException {
if (insn.getOpcode() == Opcodes.AALOAD) {
Type arrayType = value1.getType();
if (arrayType != null && arrayType.getSort() == Type.ARRAY) {
return new StrictBasicValue(AsmUtil.correctElementType(arrayType));
}
}
switch (insn.getOpcode()) {
case IALOAD:
case BALOAD:
@@ -204,7 +198,13 @@ public class OptimizationBasicInterpreter extends Interpreter<BasicValue> implem
case DREM:
return StrictBasicValue.DOUBLE_VALUE;
case AALOAD:
return StrictBasicValue.NULL_VALUE;
Type arrayType = value1.getType();
if (arrayType != null && arrayType.getSort() == Type.ARRAY) {
return new StrictBasicValue(AsmUtil.correctElementType(arrayType));
}
else {
return StrictBasicValue.NULL_VALUE;
}
case LCMP:
case FCMPL:
case FCMPG:
@@ -359,13 +359,11 @@ public class OptimizationBasicInterpreter extends Interpreter<BasicValue> implem
return StrictBasicValue.UNINITIALIZED_VALUE;
}
// if merge of two references then `lub` is java/lang/Object
// arrays also are BasicValues with reference type's
if (isReference(v) && isReference(w)) {
if (v == NULL_VALUE) return newValue(w.getType());
if (w == NULL_VALUE) return newValue(v.getType());
return StrictBasicValue.REFERENCE_VALUE;
return mergeReferenceTypes(w.getType(), v.getType());
}
// if merge of something can be stored in int var (int, char, boolean, byte, character)
@@ -380,4 +378,35 @@ public class OptimizationBasicInterpreter extends Interpreter<BasicValue> implem
private static boolean isReference(@NotNull BasicValue v) {
return v.getType().getSort() == Type.OBJECT || v.getType().getSort() == Type.ARRAY;
}
// Merge reference types, keeping track of array dimensions.
// See also org.jetbrains.org.objectweb.asm.Frame.merge.
// It's assumed that types a and b are not equal when calling this method.
private BasicValue mergeReferenceTypes(@NotNull Type a, @NotNull Type b) {
// Find out the minimal array dimension of both types.
int arrayDimensions = 0;
while (a.getSort() == Type.ARRAY && b.getSort() == Type.ARRAY) {
a = AsmUtil.correctElementType(a);
b = AsmUtil.correctElementType(b);
arrayDimensions++;
}
// Either of the two types is not an array -> result is j/l/Object.
if (arrayDimensions == 0) return REFERENCE_VALUE;
// Both of the types are arrays, and element type of one or both of them is primitive ->
// result is array of j/l/Object with one fewer dimension. E.g.
// merge([I, [Lj/l/Object;) = Lj/l/Object;
// merge([I, [S) = Lj/l/Object;
// merge([[I, [[Lj/l/Object;) = [Lj/l/Object;
if (AsmUtil.isPrimitive(a) || AsmUtil.isPrimitive(b)) {
arrayDimensions--;
}
// Result is array of j/l/Object with the computed dimension.
StringBuilder result = new StringBuilder();
while (arrayDimensions-- > 0) result.append("[");
result.append(AsmTypes.OBJECT_TYPE.getDescriptor());
return newValue(Type.getType(result.toString()));
}
}
@@ -4829,6 +4829,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/casts/kt54707.kt");
}
@Test
@TestMetadata("kt54802.kt")
public void testKt54802() throws Exception {
runTest("compiler/testData/codegen/box/casts/kt54802.kt");
}
@Test
@TestMetadata("lambdaToUnitCast.kt")
public void testLambdaToUnitCast() throws Exception {
-4
View File
@@ -1,6 +1,3 @@
// IGNORE_BACKEND: JVM
// IGNORE_LIGHT_ANALYSIS
fun box(): String =
g(arrayOf("O"))
@@ -12,4 +9,3 @@ inline fun <T> Array<out T>.f(lambda: (T) -> T): T =
inline fun <reified T> Array<out T>?.orEmpty0(): Array<out T> =
this ?: (arrayOfNulls<T>(0) as Array<T>)
+14
View File
@@ -0,0 +1,14 @@
class K {
val x: String = "OK"
}
inline fun <T> Array<out T>.ifEmpty(body: () -> Array<out T>): Array<out T> =
if (size == 0) body() else this
inline fun <T> Array<out T>.f(p: (T) -> String): String =
p(this[0])
fun box(): String =
emptyArray<K>()
.ifEmpty { arrayOf(K()) }
.f(K::x)
@@ -4697,6 +4697,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/casts/kt54707.kt");
}
@Test
@TestMetadata("kt54802.kt")
public void testKt54802() throws Exception {
runTest("compiler/testData/codegen/box/casts/kt54802.kt");
}
@Test
@TestMetadata("lambdaToUnitCast.kt")
public void testLambdaToUnitCast() throws Exception {
@@ -4829,6 +4829,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/casts/kt54707.kt");
}
@Test
@TestMetadata("kt54802.kt")
public void testKt54802() throws Exception {
runTest("compiler/testData/codegen/box/casts/kt54802.kt");
}
@Test
@TestMetadata("lambdaToUnitCast.kt")
public void testLambdaToUnitCast() throws Exception {
@@ -3958,11 +3958,6 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Casts extends AbstractLightAnalysisModeTest {
@TestMetadata("kt54707.kt")
public void ignoreKt54707() throws Exception {
runTest("compiler/testData/codegen/box/casts/kt54707.kt");
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}
@@ -4096,6 +4091,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/casts/kt54581.kt");
}
@TestMetadata("kt54707.kt")
public void testKt54707() throws Exception {
runTest("compiler/testData/codegen/box/casts/kt54707.kt");
}
@TestMetadata("kt54802.kt")
public void testKt54802() throws Exception {
runTest("compiler/testData/codegen/box/casts/kt54802.kt");
}
@TestMetadata("lambdaToUnitCast.kt")
public void testLambdaToUnitCast() throws Exception {
runTest("compiler/testData/codegen/box/casts/lambdaToUnitCast.kt");
@@ -3419,6 +3419,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/casts/kt54707.kt");
}
@Test
@TestMetadata("kt54802.kt")
public void testKt54802() throws Exception {
runTest("compiler/testData/codegen/box/casts/kt54802.kt");
}
@Test
@TestMetadata("lambdaToUnitCast.kt")
public void testLambdaToUnitCast() throws Exception {
@@ -3479,6 +3479,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/casts/kt54707.kt");
}
@Test
@TestMetadata("kt54802.kt")
public void testKt54802() throws Exception {
runTest("compiler/testData/codegen/box/casts/kt54802.kt");
}
@Test
@TestMetadata("lambdaToUnitCast.kt")
public void testLambdaToUnitCast() throws Exception {
@@ -3071,6 +3071,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
runTest("compiler/testData/codegen/box/casts/kt54707.kt");
}
@TestMetadata("kt54802.kt")
public void testKt54802() throws Exception {
runTest("compiler/testData/codegen/box/casts/kt54802.kt");
}
@TestMetadata("lambdaToUnitCast.kt")
public void testLambdaToUnitCast() throws Exception {
runTest("compiler/testData/codegen/box/casts/lambdaToUnitCast.kt");
@@ -3553,6 +3553,12 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest
runTest("compiler/testData/codegen/box/casts/kt54707.kt");
}
@Test
@TestMetadata("kt54802.kt")
public void testKt54802() throws Exception {
runTest("compiler/testData/codegen/box/casts/kt54802.kt");
}
@Test
@TestMetadata("lambdaToUnitCast.kt")
public void testLambdaToUnitCast() throws Exception {