Derek Peirce

06/17/2023, 3:10 AM
I don't know if this is precisely the right channel, as this is more about an implementation detail than the language itself, but I'd like to see Kotlin -> bytecode try more to avoid unnecessary boxing. For example, take this Kotlin code:
Copy code
val y = x.takeIf { check(x) } ?: 3
Decompiled, it becomes:
Copy code
Integer var2 = x;
      int it = ((Number)var2).intValue();
      int var4 = false;
      Integer var10000 = check(x) ? var2 : null;
      int y = var10000 != null ? var10000 : 3;
Suddenly, our
is becoming an
, but if the compiler recognized that it could treat
as, say,
int value
boolean hasValue
, it could optimize this code to instead look like:
Copy code
int x = 2;
        int var2 = x;
        boolean var4 = false;
        int var10000_value;
        boolean var10000_hasValue;
        if (check(x)) {
            var10000_value = var2;
            var10000_hasValue = true;
        } else {
            var10000_value = 0;
            var10000_hasValue = false;
        int y = var10000_hasValue ? var10000_value : 3;
I would expect further compiler passes to eventually optimize this to resemble something close to the ideal:
Copy code
val y = if (check(x)) x else 3
whose Java code is the similarly compact:
Copy code
int y = check(x) ? x : 3;
much more easily than it could optimize anything that contains boxing and unboxing.


06/17/2023, 3:19 AM
the JVM can optimize out some autoboxing,
. I haven't looked at the JIT-generated machine code to see if applies in this case, though

Derek Peirce

06/17/2023, 3:25 AM
I've found tremendously little documentation on
, what I did find suggests that it's limited to
, which is strange. I think the best use-case would be
, which can be trivially optimized as a 0=false, 1=true, 2=null byte. As a JVM flag, I doubt that
applies to compiling Android apps from Kotlin, though, which my main use-case.

Ilmir Usmanov [JB]

06/17/2023, 2:55 PM
First, do not use decompiler to figure out what happens on the bytecode level - bytecode is easy enough to read and it has full information. Second, it is not possible to figure out which bytecode sequence is more performant (especially on Android) by just looking at bytecode - after R8, which performs static optimizations, dalvik bytecode is further optimized by ART. So, measurements are needed. Third, there is no reason to eliminate autoboxing in such trivial cases, where escape analysis works the best, eliminating autoboxing for us. Unless escape analysis fails. But again, that is impossible to guess just by looking at bytecode. Fourth, there is no separate backend for Android - when you compile code for Android, you are still using JVM backend.

Derek Peirce

06/18/2023, 4:24 AM
Does Android/ART generally perform escape analysis? I'm not seeing it listed here, though I don't know how comprehensive that list is and whether or not it includes JIT optimizations, and even JIT optimizations will not help when it comes to code size. (The bytecode for
val y = x._takeIf_ *{* _check_(x) *}* ?: 3
is decently larger than the bytecode needed for the ideal
val y = if (_check_(x)) x else 3