Always pick pinned argument register in 4 bit register allocation Some older Android runtimes expect the receiver `this` to always be present in the incoming argument register. The register allocator therefore extends the live range of the `this` value to cover the entire method. Due to invoke/range instructions, the live intervals for the `this` value may be split. The split children covering invoke/range instructions are minimal by construction. All the other split children that do not overlap any invoke/range instructions must be assigned the argument's incoming register. This was already the case in 8 bit and 16 bit register allocation, but the 4 bit register allocation did not cover this case. This meant that in some cases, register allocation would materialize move instructions from the incoming argument register of `this` to a different register, which was effectively unused. This issue is avoided by ensuring that all split children not covering an invoke/range instruction are assigned the incoming argument register of `this`. Bug: b/468253695 Change-Id: Ib8c083bc2a5e5b36a02ff9353242fbe4304c2c12
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java index 0672473..5f19bfb 100644 --- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java +++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -2056,7 +2056,8 @@ // avoid move generation for the argument. if (isPinnedArgumentRegister(unhandledInterval)) { if (registerConstraint == Constants.U16BIT_MAX - || (mode.is8Bit() && registerConstraint == Constants.U8BIT_MAX)) { + || (mode.is8Bit() && registerConstraint == Constants.U8BIT_MAX) + || (mode.is4Bit() && registerConstraint == Constants.U4BIT_MAX)) { int argumentRegister = unhandledInterval.getSplitParent().getRegister(); assignFreeRegisterToUnhandledInterval(unhandledInterval, argumentRegister); return true;