Status Update
Comments
ni...@gmail.com <ni...@gmail.com> #2
Thank you for the report and reproduction. I can reproduce on a Android TV emulator.
ni...@gmail.com <ni...@gmail.com> #3
Status of current findings.
This is related to the SplitReturnRewriter
added in
Not running this code pass on java.lang.Object androidx.collection.LruCache.remove(java.lang.Object)
works around the issue. I used the following patch:
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/SplitReturnRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/SplitReturnRewriter.java
index 157d26c66a..63e0f76064 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/SplitReturnRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/SplitReturnRewriter.java
@@ -43,6 +43,10 @@ public class SplitReturnRewriter extends CodeRewriterPass<AppInfo> {
@Override
protected CodeRewriterResult rewriteCode(IRCode code) {
+ if (code.context().toSourceString().equals(
+ "java.lang.Object androidx.collection.LruCache.remove(java.lang.Object)")) {
+ return CodeRewriterResult.NO_CHANGE;
+ }
int color = colorExceptionHandlers(code);
AffectedValues affectedValues = new AffectedValues();
boolean changed = false;
Comparing the DEX with and without this patch shows one instruction diff in void androidx.leanback.widget.GridLayoutManager$2.addItem(java.lang.Object, int, int, int, int)
where java.lang.Object androidx.collection.LruCache.remove(java.lang.Object)
is inlined into.
Tracing the IR for void androidx.leanback.widget.GridLayoutManager$2.addItem(java.lang.Object, int, int, int, int)
for the two versions shows that they start to deviate in second pass of IR after inserting assume instructions (SSA)
with
block 29, pred-counts: 1, succ-count: 2, filled: true, sealed: false
predecessors: 28
successors: 31 30 (no try/catch successors)
no phis
#143:loadView;1762:addItem: -1: InstanceGet v67 <- v136; field: androidx.collection.LruCache androidx.leanback.widget.ViewsStateBundle.mChildStates
: -1: Assume v140 <- v67; lower bound: @Nullable androidx.collection.LruCache {}
: -1: SafeCheckCast v131 <- v65; java.lang.String
: -1: Invoke-Virtual v132 <- v140, v131; method: java.lang.Object androidx.collection.LruCache.remove(java.lang.String)
: -1: CheckCast v69 <- v132; android.util.SparseArray
#144:loadView;1762:addItem: -1: If v69 EQZ block 31 (fallthrough 30)
vs.
block 29, pred-counts: 1, succ-count: 2, filled: true, sealed: false
predecessors: 28
successors: 31 30 (no try/catch successors)
no phis
#143:loadView;1762:addItem: -1: InstanceGet v67 <- v136; field: androidx.collection.LruCache androidx.leanback.widget.ViewsStateBundle.mChildStates
: -1: Assume v140 <- v67; lower bound: @Nullable androidx.collection.LruCache {}
: -1: SafeCheckCast v131 <- v65; java.lang.String
: -1: Invoke-Virtual v132 <- v140, v131; method: java.lang.Object androidx.collection.LruCache.remove(java.lang.String)
: -1: Assume v141 <- v132; not null
: -1: CheckCast v69 <- v141; android.util.SparseArray
#144:loadView;1762:addItem: -1: If v69 EQZ block 31 (fallthrough 30)
This is from computeDynamicReturnType
concluding a dynamic return type of DynamicTypeWithUpperBound(@NotNull java.lang.Object {})
.
ak...@google.com <ak...@google.com>
ak...@google.com <ak...@google.com> #4
Looks like dead code removal in java.lang.Object androidx.collection.LruCache.remove(java.lang.Object)
is causing this.
The method java.lang.Object androidx.collection.LruCache.remove(java.lang.Object)
is processed twice in first round of IR, second time with more precise type for the argument as java.lang.Object androidx.collection.LruCache.remove(java.lang.String)
.
That second processing involves quite a bit of inlining. However before dead code removal the return is still split between null and not null return value:
block 33, pred-counts: 1, succ-count: 2, filled: true, sealed: false
predecessors: 32
successors: 37 34 (no try/catch successors)
no phis
#187:remove;0:remove: -1: If v54 EQZ block 37 (fallthrough 34)
block 34, pred-counts: 1, succ-count: 1, filled: true, sealed: false
predecessors: 33
successors: 35 (no try/catch successors)
no phis
#187:remove;0:remove: -1: Assume v49 <- v54; not null
#188:remove;0:remove: -1: ConstNumber v32(0b0) <- 0 (INT)
: -1: ConstNumber v33(0) <- 0 (@Null NULL)
: -1: Goto block 35
block 35, pred-counts: 1, succ-count: 0, filled: true, sealed: false
predecessors: 34
successors: -
no phis
#188:remove;0:remove: -1: ConstString v90 <- "key"
: -1: Invoke-Static v45, v90; method: void kotlin.jvm.internal.Intrinsics.checkNotNullParameter(java.lang.Object, java.lang.String)
: -1: ConstString v91 <- "oldValue"
: -1: Invoke-Static v49, v91; method: void kotlin.jvm.internal.Intrinsics.checkNotNullParameter(java.lang.Object, java.lang.String)
#191:remove;0:remove: -1: Return v49
block 37, pred-counts: 1, succ-count: 0, filled: true, sealed: false
predecessors: 33
successors: -
no phis
#191:remove;0:remove: -1: Return v54
After dead code removal a single return is left with the non null assumption:
block 34, pred-counts: 1, succ-count: 1, filled: true, sealed: false
predecessors: 32
successors: 35 (no try/catch successors)
no phis
#187:remove;0:remove: -1: Assume v49 <- v54; not null
#188:remove;0:remove: -1: Goto block 35
block 35, pred-counts: 1, succ-count: 0, filled: true, sealed: false
predecessors: 34
successors: -
no phis
#191:remove;0:remove: -1: Return v49
For reference the source of the method is:
public fun remove(key: K): V? {
val previous: V?
lock.synchronized {
previous = map.remove(key)
if (previous != null) {
size -= safeSizeOf(key, previous)
}
}
if (previous != null) {
entryRemoved(false, key, previous, null)
}
return previous
}
with the default implementation of entryRemoved
doing nothing:
protected open fun entryRemoved(evicted: Boolean, key: K, oldValue: V, newValue: V?) {}
ni...@gmail.com <ni...@gmail.com> #5
The underlying issue is that branch simplification (as part of dead code removal) is comparing return values through their aliased values (in valuesAreIdentical
) potentially conflating return values with different assume instructions. As this happens before collecting optimization info this can lead to the wrong dynamic type for the return value.
private boolean valuesAreIdentical(Value value, Value other) {
value = value.getAliasedValue();
other = other.getAliasedValue();
if (value == other) {
return true;
}
if (value.isPhi() || other.isPhi()) {
return false;
}
return instructionsDefineIdenticalValues(value.definition, other.definition);
}
Description
What
User experience
What type of issue is this?
Other issue
What steps would let us observe this issue?
What did you expect to happen?
Immediate pop up of Sim card options to make a call
What actually happened?
Delay in the pop up of the Sim card options
What was the effect of this issue on your device usage, such as lost time or work?
High
When
Time and frequency
Time when bug report was triggered: Nov 13, 2024 11:36 AM GMT+05:30
How often has this happened?
Every time
Where
Component
Suggested component: <not visible> (1630575)
Build and device data
- Build Number: google/panther_beta/panther:15/BP11.241025.006/12620009:user/release-keys
(Note: It is the build when sending this report. For exact build reference, please see the attached bugreport.)
Related apps
Phone
com.google.android.dialer
Version 18055868 (152.0.690390391)
System App (Updated)
Debugging information
Google Play services
com.google.android.gms
Version 244337035 (24.43.37 (260400-693941914))
System App (Updated)
Android System WebView
com.google.android.webview
Version 672310733 (130.0.6723.107)
System App (Updated)
Network operator: AirTel
SIM operator: airtel
Network operator: JIO 4G
SIM operator: Jio
Filed by Android Beta Feedback. Version (Updated): 2.46-betterbug.external_20241023_RC01 (DOGFOOD)https://developer.android.com/preview/feedback#feedback-app .
To learn more about our feedback process, please visit