Skip to content

Commit e7f77e9

Browse files
committed
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
1 parent 7deaab9 commit e7f77e9

File tree

11 files changed

+104
-20
lines changed

11 files changed

+104
-20
lines changed

compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/OptimizationBasicInterpreter.java

+39-11
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.jetbrains.annotations.NotNull;
2020
import org.jetbrains.annotations.Nullable;
2121
import org.jetbrains.kotlin.codegen.AsmUtil;
22+
import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
2223
import org.jetbrains.org.objectweb.asm.Handle;
2324
import org.jetbrains.org.objectweb.asm.Opcodes;
2425
import org.jetbrains.org.objectweb.asm.Type;
@@ -152,13 +153,6 @@ public BasicValue binaryOperation(
152153
@NotNull BasicValue value1,
153154
@NotNull BasicValue value2
154155
) throws AnalyzerException {
155-
if (insn.getOpcode() == Opcodes.AALOAD) {
156-
Type arrayType = value1.getType();
157-
if (arrayType != null && arrayType.getSort() == Type.ARRAY) {
158-
return new StrictBasicValue(AsmUtil.correctElementType(arrayType));
159-
}
160-
}
161-
162156
switch (insn.getOpcode()) {
163157
case IALOAD:
164158
case BALOAD:
@@ -204,7 +198,13 @@ public BasicValue binaryOperation(
204198
case DREM:
205199
return StrictBasicValue.DOUBLE_VALUE;
206200
case AALOAD:
207-
return StrictBasicValue.NULL_VALUE;
201+
Type arrayType = value1.getType();
202+
if (arrayType != null && arrayType.getSort() == Type.ARRAY) {
203+
return new StrictBasicValue(AsmUtil.correctElementType(arrayType));
204+
}
205+
else {
206+
return StrictBasicValue.NULL_VALUE;
207+
}
208208
case LCMP:
209209
case FCMPL:
210210
case FCMPG:
@@ -359,13 +359,11 @@ public BasicValue merge(
359359
return StrictBasicValue.UNINITIALIZED_VALUE;
360360
}
361361

362-
// if merge of two references then `lub` is java/lang/Object
363-
// arrays also are BasicValues with reference type's
364362
if (isReference(v) && isReference(w)) {
365363
if (v == NULL_VALUE) return newValue(w.getType());
366364
if (w == NULL_VALUE) return newValue(v.getType());
367365

368-
return StrictBasicValue.REFERENCE_VALUE;
366+
return mergeReferenceTypes(w.getType(), v.getType());
369367
}
370368

371369
// if merge of something can be stored in int var (int, char, boolean, byte, character)
@@ -380,4 +378,34 @@ public BasicValue merge(
380378
private static boolean isReference(@NotNull BasicValue v) {
381379
return v.getType().getSort() == Type.OBJECT || v.getType().getSort() == Type.ARRAY;
382380
}
381+
382+
// Merge reference types, keeping track of array dimensions.
383+
// See also org.jetbrains.org.objectweb.asm.Frame.merge.
384+
private BasicValue mergeReferenceTypes(@NotNull Type a, @NotNull Type b) {
385+
// Find out the minimal array dimension of both types.
386+
int arrayDimensions = 0;
387+
while (a.getSort() == Type.ARRAY && b.getSort() == Type.ARRAY) {
388+
a = AsmUtil.correctElementType(a);
389+
b = AsmUtil.correctElementType(b);
390+
arrayDimensions++;
391+
}
392+
// Either of the two types is not an array -> result is j/l/Object.
393+
if (arrayDimensions == 0) return REFERENCE_VALUE;
394+
395+
// Both of the types are arrays, and element type of one or both of them is primitive ->
396+
// result is array of j/l/Object with one fewer dimension. E.g.
397+
// merge([I, [Lj/l/Object;) = Lj/l/Object;
398+
// merge([I, [S) = Lj/l/Object;
399+
// merge([[I, [[Lj/l/Object;) = [Lj/l/Object;
400+
if (AsmUtil.isPrimitive(a) || AsmUtil.isPrimitive(b)) {
401+
arrayDimensions--;
402+
}
403+
404+
// Result is array of j/l/Object with the computed dimension.
405+
StringBuilder result = new StringBuilder();
406+
while (arrayDimensions-- > 0) result.append("[");
407+
result.append(AsmTypes.OBJECT_TYPE.getDescriptor());
408+
return newValue(Type.getType(result.toString()));
409+
}
410+
383411
}

compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// IGNORE_BACKEND: JVM
2-
// IGNORE_LIGHT_ANALYSIS
3-
41
fun box(): String =
52
g(arrayOf("O"))
63

@@ -12,4 +9,3 @@ inline fun <T> Array<out T>.f(lambda: (T) -> T): T =
129

1310
inline fun <reified T> Array<out T>?.orEmpty0(): Array<out T> =
1411
this ?: (arrayOfNulls<T>(0) as Array<T>)
15-
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class K {
2+
val x: String = "OK"
3+
}
4+
5+
inline fun <T> Array<out T>.ifEmpty(body: () -> Array<out T>): Array<out T> =
6+
if (size == 0) body() else this
7+
8+
inline fun <T> Array<out T>.f(p: (T) -> String): String =
9+
p(this[0])
10+
11+
fun box(): String =
12+
emptyArray<K>()
13+
.ifEmpty { arrayOf(K()) }
14+
.f(K::x)

compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java

+10-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)