Merge changes I6e4c0eb8,I609082ab,If9b7306f,Ibed2bdab,I21eab092 into androidx-main

* changes:
  Select the postview size by the ResolutionSelector
  Add onPostviewImageAvailable to ImageCapture callback and configure the capture pipeline to enable postview.
  Postview implementation in basic and advanced extender.
  Add postviewSurface and postviewEnabled to SessionConfig / CaptureConfig
  Add onCaptureProcessProgressed callback to takePicture and fix the onCaptureStarted timeing when Extensions are enabled.
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/BenchmarkState.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/BenchmarkState.kt
index da93c8f..f85d140 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/BenchmarkState.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/BenchmarkState.kt
@@ -18,6 +18,7 @@
 
 import android.annotation.SuppressLint
 import android.os.Bundle
+import android.os.Looper
 import android.util.Log
 import androidx.annotation.IntRange
 import androidx.annotation.RestrictTo
@@ -287,7 +288,19 @@
         iterationsPerRepeat = iterationsPerRepeat.coerceAtLeast(currentLoopsPerMeasurement)
 
         InMemoryTracing.beginSection(currentPhase.label)
-        val phaseProfilerResult = currentPhase.profiler?.start(traceUniqueName)
+        val phaseProfilerResult = currentPhase.profiler?.run {
+            val estimatedMethodTraceDurNs =
+                warmupEstimatedIterationTimeNs * METHOD_TRACING_ESTIMATED_SLOWDOWN_FACTOR
+            if (this == MethodTracing &&
+                Looper.myLooper() == Looper.getMainLooper() &&
+                estimatedMethodTraceDurNs > METHOD_TRACING_MAX_DURATION_NS) {
+                Log.d(TAG, "Skipping method trace of estimated duration" +
+                    " ${estimatedMethodTraceDurNs / 1_000_000_000.0} sec to avoid ANR")
+                null
+            } else {
+                start(traceUniqueName)
+            }
+        }
         if (phaseProfilerResult != null) {
             require(profilerResult == null) {
                 "ProfileResult already set, only support one profiling phase"
@@ -387,15 +400,30 @@
      */
     private inline fun check(value: Boolean, lazyMessage: () -> String) {
         if (!value) {
-            ThreadPriority.resetBumpedThread()
-            if (phaseIndex >= 0 && phaseIndex <= phases.size) {
-                InMemoryTracing.endSection() // current phase cancelled, complete trace event
-            }
+            cleanupBeforeThrow()
             throw IllegalStateException(lazyMessage())
         }
     }
 
     /**
+     * Ideally this would only be called when an exception is observed in measureRepeated, but to
+     * account for java callers, we explicitly trigger before throwing as well.
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    fun cleanupBeforeThrow() {
+        if (phaseIndex >= 0 && phaseIndex <= phases.size) {
+            Log.d(TAG, "aborting and cancelling benchmark")
+            // current phase cancelled, complete current phase cleanup (trace event and profiling)
+            InMemoryTracing.endSection()
+            currentPhase.profiler?.stop()
+
+            // for safety, set other state to done and do broader cleanup
+            phaseIndex = phases.size
+            afterBenchmark()
+        }
+    }
+
+    /**
      * Internal loop control for benchmarks - will return true as long as there are more
      * measurements to perform.
      *
@@ -549,6 +577,24 @@
 
         internal const val REPEAT_COUNT_ALLOCATION = 5
 
+        /**
+         * Conservative estimate for how much method tracing slows down runtime
+         * how much longer will `methodTrace {x()}` be than `x()`
+         *
+         * This is a conservative estimate, better version of this would account for OS/Art version
+         *
+         * Value derived from observed numbers on bramble API 31 (600-800x slowdown)
+         */
+        internal const val METHOD_TRACING_ESTIMATED_SLOWDOWN_FACTOR = 1000
+
+        /**
+         * Maximum duration to trace on main thread to avoid ANRs
+         *
+         * In practice, other types of tracing can be equally dangerous for ANRs,
+         * but method tracing is the default tracing mode.
+         */
+        internal const val METHOD_TRACING_MAX_DURATION_NS = 4_000_000_000
+
         internal val DEFAULT_MEASUREMENT_DURATION_NS = TimeUnit.MILLISECONDS.toNanos(100)
         internal val SAMPLED_PROFILER_DURATION_NS =
             TimeUnit.SECONDS.toNanos(Arguments.profilerSampleDurationSeconds)
diff --git a/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt b/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt
index c6502ef..3ffde67 100644
--- a/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt
+++ b/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt
@@ -303,8 +303,13 @@
     val localState = getState()
     val localScope = scope
 
-    while (localState.keepRunningInline()) {
-        block(localScope)
+    try {
+        while (localState.keepRunningInline()) {
+            block(localScope)
+        }
+    } catch (t: Throwable) {
+        localState.cleanupBeforeThrow()
+        throw t
     }
 }
 
@@ -383,6 +388,7 @@
                 localState.pauseTiming()
 
                 if (timeNs > hardDeadlineNs) {
+                    localState.cleanupBeforeThrow()
                     val overrunInSec = (timeNs - hardDeadlineNs) / 1_000_000_000.0
                     throw IllegalStateException(
                         "Benchmark loop overran hard time limit by $overrunInSec seconds"
diff --git a/bluetooth/bluetooth-testing/src/test/kotlin/androidx/bluetooth/testing/RobolectricGattServerTest.kt b/bluetooth/bluetooth-testing/src/test/kotlin/androidx/bluetooth/testing/RobolectricGattServerTest.kt
index 587fd4d..ac988ae 100644
--- a/bluetooth/bluetooth-testing/src/test/kotlin/androidx/bluetooth/testing/RobolectricGattServerTest.kt
+++ b/bluetooth/bluetooth-testing/src/test/kotlin/androidx/bluetooth/testing/RobolectricGattServerTest.kt
@@ -25,6 +25,7 @@
 import android.bluetooth.BluetoothGattServerCallback
 import android.bluetooth.BluetoothGattService as FwkService
 import android.bluetooth.BluetoothManager
+import android.bluetooth.BluetoothStatusCodes as FwkBluetoothStatusCodes
 import android.content.Context
 import androidx.bluetooth.BluetoothLe
 import androidx.bluetooth.GattCharacteristic
@@ -40,11 +41,9 @@
 import junit.framework.TestCase.fail
 import kotlin.test.assertFailsWith
 import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.cancel
+import kotlinx.coroutines.TimeoutCancellationException
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.takeWhile
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.test.runTest
 import kotlinx.coroutines.withTimeout
 import org.junit.Assert.assertEquals
@@ -142,30 +141,28 @@
         val device = createDevice("00:11:22:33:44:55")
         val closed = CompletableDeferred()
 
-        runAfterServicesAreAdded(services.size) {
-            connectDevice(device) {
-                serverAdapter.callback.onCharacteristicReadRequest(
-                    device, /*requestId=*/1, /*offset=*/0, readCharacteristic.fwkCharacteristic)
-            }
-        }
         serverAdapter.onCloseGattServerListener =
             StubServerFrameworkAdapter.OnCloseGattServerListener {
                 closed.complete(Unit)
             }
 
-        launch {
-            bluetoothLe.openGattServer(services).first().let {
-                it.reject()
-                assertThrows(IllegalStateException::class.java) {
-                    runBlocking {
-                        it.accept {}
-                    }
+        bluetoothLe.openGattServer(services)
+            .onOpened {
+                connectDevice(device) {
+                    serverAdapter.callback.onCharacteristicReadRequest(
+                        device, /*requestId=*/1, /*offset=*/0, readCharacteristic.fwkCharacteristic)
                 }
             }
-        }.join()
-
-        assertTrue(closed.isCompleted)
-        assertEquals(0, serverAdapter.shadowGattServer.responses.size)
+            .onClosed {
+                assertTrue(closed.isCompleted)
+                assertEquals(0, serverAdapter.shadowGattServer.responses.size)
+            }
+            .first().let {
+                it.reject()
+                assertFailsWith {
+                    it.accept {}
+                }
+            }
     }
 
     @Test
@@ -174,28 +171,29 @@
         val device = createDevice("00:11:22:33:44:55")
         val closed = CompletableDeferred()
 
-        runAfterServicesAreAdded(services.size) {
-            connectDevice(device) {
-                serverAdapter.callback.onCharacteristicReadRequest(
-                    device, /*requestId=*/1, /*offset=*/0, readCharacteristic.fwkCharacteristic)
-            }
-        }
         serverAdapter.onCloseGattServerListener =
             StubServerFrameworkAdapter.OnCloseGattServerListener {
                 closed.complete(Unit)
             }
 
-        launch {
-            bluetoothLe.openGattServer(services).first().let {
+        bluetoothLe.openGattServer(services)
+            .onOpened {
+                connectDevice(device) {
+                    serverAdapter.callback.onCharacteristicReadRequest(
+                        device, /*requestId=*/1, /*offset=*/0, readCharacteristic.fwkCharacteristic
+                    )
+                }
+            }
+            .onClosed {
+                assertTrue(closed.isCompleted)
+                assertEquals(0, serverAdapter.shadowGattServer.responses.size)
+            }
+            .first().let {
                 it.accept {}
                 assertThrows(IllegalStateException::class.java) {
                     it.reject()
                 }
             }
-        }.join()
-
-        assertTrue(closed.isCompleted)
-        assertEquals(0, serverAdapter.shadowGattServer.responses.size)
     }
 
     @Test
@@ -205,19 +203,29 @@
         val closed = CompletableDeferred()
         val valueToRead = 42
 
-        runAfterServicesAreAdded(services.size) {
-            connectDevice(device) {
-                serverAdapter.callback.onCharacteristicReadRequest(
-                    device, /*requestId=*/1, /*offset=*/0, readCharacteristic.fwkCharacteristic)
-            }
-        }
         serverAdapter.onCloseGattServerListener =
             StubServerFrameworkAdapter.OnCloseGattServerListener {
                 closed.complete(Unit)
             }
 
-        launch {
-            bluetoothLe.openGattServer(services).collect {
+        bluetoothLe.openGattServer(services)
+            .onOpened {
+                connectDevice(device) {
+                    serverAdapter.callback.onCharacteristicReadRequest(
+                        device, /*requestId=*/
+                        1, /*offset=*/
+                        0,
+                        readCharacteristic.fwkCharacteristic
+                    )
+                }
+            }
+            .onClosed {
+                // Ensure if the server is closed
+                assertTrue(closed.isCompleted)
+                assertEquals(1, serverAdapter.shadowGattServer.responses.size)
+                assertEquals(valueToRead, serverAdapter.shadowGattServer.responses[0].toInt())
+            }
+            .first().let {
                 it.accept {
                     when (val request = requests.first()) {
                         is GattServerRequest.ReadCharacteristic -> {
@@ -225,16 +233,8 @@
                         }
                         else -> fail("unexpected request")
                     }
-                    // Close the server
-                    [email protected]()
                 }
             }
-        }.join()
-
-        // Ensure if the server is closed
-        assertTrue(closed.isCompleted)
-        assertEquals(1, serverAdapter.shadowGattServer.responses.size)
-        assertEquals(valueToRead, serverAdapter.shadowGattServer.responses[0].toInt())
     }
 
     @Test
@@ -244,12 +244,6 @@
         val closed = CompletableDeferred()
         val responsed = CompletableDeferred()
 
-        runAfterServicesAreAdded(services.size) {
-            connectDevice(device) {
-                serverAdapter.callback.onCharacteristicReadRequest(
-                    device, /*requestId=*/1, /*offset=*/0, readCharacteristic.fwkCharacteristic)
-            }
-        }
         serverAdapter.onCloseGattServerListener =
             StubServerFrameworkAdapter.OnCloseGattServerListener {
                 closed.complete(Unit)
@@ -262,8 +256,23 @@
                 responsed.complete(Unit)
             }
 
-        launch {
-            bluetoothLe.openGattServer(services).collect {
+        bluetoothLe.openGattServer(services)
+            .onOpened {
+                connectDevice(device) {
+                    serverAdapter.callback.onCharacteristicReadRequest(
+                        device, /*requestId=*/
+                        1, /*offset=*/
+                        0,
+                        readCharacteristic.fwkCharacteristic
+                    )
+                }
+            }
+            .onClosed {
+                // Ensure if the server is closed
+                assertTrue(closed.isCompleted)
+                assertTrue(responsed.isCompleted)
+            }
+            .first().let {
                 it.accept {
                     when (val request = requests.first()) {
                         is GattServerRequest.ReadCharacteristic -> {
@@ -271,15 +280,8 @@
                         }
                         else -> fail("unexpected request")
                     }
-                    // Close the server
-                    [email protected]()
                 }
             }
-        }.join()
-
-        // Ensure if the server is closed
-        assertTrue(closed.isCompleted)
-        assertTrue(responsed.isCompleted)
     }
 
     @Test
@@ -289,21 +291,29 @@
         val closed = CompletableDeferred()
         val valueToRead = 42
 
-        runAfterServicesAreAdded(services.size) {
-            connectDevice(device) {
-                serverAdapter.callback.onCharacteristicReadRequest(
-                    device, /*requestId=*/1, /*offset=*/0, unknownCharacteristic.fwkCharacteristic)
-                serverAdapter.callback.onCharacteristicReadRequest(
-                    device, /*requestId=*/2, /*offset=*/0, readCharacteristic.fwkCharacteristic)
-            }
-        }
         serverAdapter.onCloseGattServerListener =
             StubServerFrameworkAdapter.OnCloseGattServerListener {
                 closed.complete(Unit)
             }
 
-        launch {
-            bluetoothLe.openGattServer(services).collect {
+        bluetoothLe.openGattServer(services)
+            .onOpened {
+                connectDevice(device) {
+                    serverAdapter.callback.onCharacteristicReadRequest(
+                        device, /*requestId=*/
+                        1, /*offset=*/
+                        0,
+                        unknownCharacteristic.fwkCharacteristic
+                    )
+                    serverAdapter.callback.onCharacteristicReadRequest(
+                        device, /*requestId=*/2, /*offset=*/0, readCharacteristic.fwkCharacteristic
+                    )
+                }
+            }
+            .onClosed {
+                assertTrue(closed.isCompleted)
+            }
+            .first().let {
                 it.accept {
                     when (val request = requests.first()) {
                         is GattServerRequest.ReadCharacteristic -> {
@@ -313,14 +323,10 @@
 
                         else -> fail("unexpected request")
                     }
-                    // Close the server
-                    [email protected]()
                 }
             }
-        }.join()
-
-        assertTrue(closed.isCompleted)
     }
+
     @Test
     fun writeCharacteristic() = runTest {
         val services = listOf(service1, service2)
@@ -328,21 +334,24 @@
         val closed = CompletableDeferred()
         val valueToWrite = 42
 
-        runAfterServicesAreAdded(services.size) {
-            connectDevice(device) {
-                serverAdapter.callback.onCharacteristicWriteRequest(
-                    device, /*requestId=*/1, writeCharacteristic.fwkCharacteristic,
-                    /*preparedWrite=*/false, /*responseNeeded=*/false,
-                    /*offset=*/0, valueToWrite.toByteArray())
-            }
-        }
         serverAdapter.onCloseGattServerListener =
             StubServerFrameworkAdapter.OnCloseGattServerListener {
                 closed.complete(Unit)
             }
 
-        launch {
-            bluetoothLe.openGattServer(services).collect {
+        bluetoothLe.openGattServer(services)
+            .onOpened {
+                connectDevice(device) {
+                    serverAdapter.callback.onCharacteristicWriteRequest(
+                        device, /*requestId=*/1, writeCharacteristic.fwkCharacteristic,
+                        /*preparedWrite=*/false, /*responseNeeded=*/false,
+                        /*offset=*/0, valueToWrite.toByteArray())
+                }
+            }
+            .onClosed {
+                assertTrue(closed.isCompleted)
+            }
+            .first().let {
                 it.accept {
                     when (val request = requests.first()) {
                         is GattServerRequest.WriteCharacteristics -> {
@@ -352,13 +361,8 @@
 
                         else -> fail("unexpected request")
                     }
-                    // Close the server
-                    [email protected]()
                 }
             }
-        }.join()
-
-        assertTrue(closed.isCompleted)
     }
 
     @Test
@@ -369,15 +373,6 @@
         val responded = CompletableDeferred()
         val valueToWrite = 42
 
-        runAfterServicesAreAdded(services.size) {
-            connectDevice(device) {
-                serverAdapter.callback.onCharacteristicWriteRequest(
-                    device, /*requestId=*/1, writeCharacteristic.fwkCharacteristic,
-                    /*preparedWrite=*/false, /*responseNeeded=*/false,
-                    /*offset=*/0, valueToWrite.toByteArray()
-                )
-            }
-        }
         serverAdapter.onCloseGattServerListener =
             StubServerFrameworkAdapter.OnCloseGattServerListener {
                 closed.complete(Unit)
@@ -390,8 +385,20 @@
                 responded.complete(Unit)
             }
 
-        launch {
-            bluetoothLe.openGattServer(services).collect {
+        bluetoothLe.openGattServer(services)
+            .onOpened {
+                connectDevice(device) {
+                    serverAdapter.callback.onCharacteristicWriteRequest(
+                        device, /*requestId=*/1, writeCharacteristic.fwkCharacteristic,
+                        /*preparedWrite=*/false, /*responseNeeded=*/false,
+                        /*offset=*/0, valueToWrite.toByteArray()
+                    )
+                }
+            }
+            .onClosed {
+                assertTrue(closed.isCompleted)
+            }
+            .first().let {
                 it.accept {
                     when (val request = requests.first()) {
                         is GattServerRequest.WriteCharacteristics -> {
@@ -401,13 +408,8 @@
 
                         else -> fail("unexpected request")
                     }
-                    // Close the server
-                    [email protected]()
                 }
             }
-        }.join()
-
-        assertTrue(closed.isCompleted)
     }
 
     @Test
@@ -418,12 +420,6 @@
         val closed = CompletableDeferred()
         val valueToNotify = 42
 
-        runAfterServicesAreAdded(services.size) {
-            connectDevice(device) {
-                serverAdapter.callback.onCharacteristicReadRequest(
-                    device, /*requestId=*/1, /*offset=*/0, readCharacteristic.fwkCharacteristic)
-            }
-        }
         serverAdapter.onNotifyCharacteristicChangedListener =
             StubServerFrameworkAdapter.OnNotifyCharacteristicChangedListener {
                     fwkDevice, _, _, value ->
@@ -435,19 +431,23 @@
                 closed.complete(Unit)
             }
 
-        launch {
-            bluetoothLe.openGattServer(services).collect {
-                it.accept {
-                    notify(notifyCharacteristic, valueToNotify.toByteArray())
-                    // Close the server
-                    [email protected]()
+        bluetoothLe.openGattServer(services)
+            .onOpened {
+                connectDevice(device) {
+                    serverAdapter.callback.onCharacteristicReadRequest(
+                        device, /*requestId=*/1, /*offset=*/0, readCharacteristic.fwkCharacteristic)
                 }
             }
-        }.join()
-
-        // Ensure if the server is closed
-        assertTrue(closed.isCompleted)
-        assertEquals(valueToNotify, notified.await())
+            .onClosed {
+                // Ensure if the server is closed
+                assertTrue(closed.isCompleted)
+                assertEquals(valueToNotify, notified.await())
+            }
+            .first().let {
+                it.accept {
+                    notify(notifyCharacteristic, valueToNotify.toByteArray())
+                }
+            }
     }
 
     @Test
@@ -457,12 +457,6 @@
         val closed = CompletableDeferred()
         val tooLongValue = ByteBuffer.allocate(513).array()
 
-        runAfterServicesAreAdded(services.size) {
-            connectDevice(device) {
-                serverAdapter.callback.onCharacteristicReadRequest(
-                    device, /*requestId=*/1, /*offset=*/0, readCharacteristic.fwkCharacteristic)
-            }
-        }
         serverAdapter.onNotifyCharacteristicChangedListener =
             StubServerFrameworkAdapter.OnNotifyCharacteristicChangedListener {
                     _, _, _, _ ->
@@ -473,20 +467,24 @@
                 closed.complete(Unit)
             }
 
-        launch {
-            bluetoothLe.openGattServer(services).collect {
+        bluetoothLe.openGattServer(services)
+            .onOpened {
+                connectDevice(device) {
+                    serverAdapter.callback.onCharacteristicReadRequest(
+                        device, /*requestId=*/1, /*offset=*/0, readCharacteristic.fwkCharacteristic)
+                }
+            }
+            .onClosed {
+                // Ensure if the server is closed
+                assertTrue(closed.isCompleted)
+            }
+            .first().let {
                 it.accept {
                     assertFailsWith {
                         notify(notifyCharacteristic, tooLongValue)
                     }
-                    // Close the server
-                    [email protected]()
                 }
             }
-        }.join()
-
-        // Ensure if the server is closed
-        assertTrue(closed.isCompleted)
     }
 
     @Test
@@ -494,7 +492,7 @@
         val services = listOf(service1, service2)
         val device = createDevice("00:11:22:33:44:55")
 
-        runAfterServicesAreAdded(services.size) {
+        bluetoothLe.openGattServer(services).onOpened {
             connectDevice(device) {
                 serverAdapter.callback.onCharacteristicReadRequest(
                     device, /*requestId=*/1, /*offset=*/0, readCharacteristic.fwkCharacteristic)
@@ -515,20 +513,14 @@
                     /*value=*/FwkDescriptor.ENABLE_INDICATION_VALUE
                 )
             }
-        }
-
-        launch {
-            bluetoothLe.openGattServer(services).collect {
-                it.accept {
-                    val characteristics = subscribedCharacteristics
-                        .takeWhile { chars -> chars.size == 2 }.first()
-                    assertTrue(characteristics.contains(notifyCharacteristic))
-                    assertTrue(characteristics.contains(indicateCharacteristic))
-                    // Close the server
-                    [email protected]()
-                }
+        }.first().let {
+            it.accept {
+                val characteristics = subscribedCharacteristics
+                    .takeWhile { chars -> chars.size == 2 }.first()
+                assertTrue(characteristics.contains(notifyCharacteristic))
+                assertTrue(characteristics.contains(indicateCharacteristic))
             }
-        }.join()
+        }
     }
 
     @Test
@@ -536,34 +528,33 @@
         val services = listOf(service1, service2)
         val device = createDevice("00:11:22:33:44:55")
 
-        runAfterServicesAreAdded(services.size) {
-            connectDevice(device) {
-                serverAdapter.callback.onCharacteristicReadRequest(
-                    device, /*requestId=*/1, /*offset=*/0, readCharacteristic.fwkCharacteristic)
-                serverAdapter.callback.onDescriptorWriteRequest(
-                    device, /*requestId=*/2,
-                    notifyCharacteristic.fwkCharacteristic.getDescriptor(cccDescriptorUuid),
-                    /*preparedWrite=*/false,
-                    /*responseNeeded=*/false,
-                    /*offset=*/0,
-                    /*value=*/FwkDescriptor.ENABLE_INDICATION_VALUE
-                )
-                serverAdapter.callback.onDescriptorWriteRequest(
-                    device, /*requestId=*/3,
-                    indicateCharacteristic.fwkCharacteristic.getDescriptor(cccDescriptorUuid),
-                    /*preparedWrite=*/false,
-                    /*responseNeeded=*/false,
-                    /*offset=*/0,
-                    /*value=*/FwkDescriptor.ENABLE_NOTIFICATION_VALUE
-                )
+        bluetoothLe.openGattServer(services)
+            .onOpened {
+                connectDevice(device) {
+                    serverAdapter.callback.onCharacteristicReadRequest(
+                        device, /*requestId=*/1, /*offset=*/0, readCharacteristic.fwkCharacteristic)
+                    serverAdapter.callback.onDescriptorWriteRequest(
+                        device, /*requestId=*/2,
+                        notifyCharacteristic.fwkCharacteristic.getDescriptor(cccDescriptorUuid),
+                        /*preparedWrite=*/false,
+                        /*responseNeeded=*/false,
+                        /*offset=*/0,
+                        /*value=*/FwkDescriptor.ENABLE_INDICATION_VALUE
+                    )
+                    serverAdapter.callback.onDescriptorWriteRequest(
+                        device, /*requestId=*/3,
+                        indicateCharacteristic.fwkCharacteristic.getDescriptor(cccDescriptorUuid),
+                        /*preparedWrite=*/false,
+                        /*responseNeeded=*/false,
+                        /*offset=*/0,
+                        /*value=*/FwkDescriptor.ENABLE_NOTIFICATION_VALUE
+                    )
+                }
             }
-        }
-
-        launch {
-            bluetoothLe.openGattServer(services).collect {
+            .first().let {
                 it.accept {
-                    runBlocking {
-                        withTimeout(1_000) {
+                    assertFailsWith {
+                        withTimeout(200) {
                             subscribedCharacteristics.collect { chars ->
                                 assertTrue(chars.isEmpty())
                             }
@@ -571,7 +562,6 @@
                     }
                 }
             }
-        }.join()
     }
 
     @Test
@@ -589,11 +579,13 @@
                 closed.complete(Unit)
             }
 
-        launch {
-            val serverFlow = bluetoothLe.openGattServer(listOf(service1))
+        val serverFlow = bluetoothLe.openGattServer(listOf(service1))
+        serverFlow.onOpened {
             serverFlow.updateServices(listOf(service2))
-            serverFlow.first().accept {}
-        }.join()
+        }
+            .first().let {
+                it.accept {}
+            }
 
         assertTrue(opened.isCompleted)
         assertTrue(closed.isCompleted)
@@ -606,27 +598,34 @@
         val closed = CompletableDeferred()
         val values = listOf(byteArrayOf(0, 1), byteArrayOf(2, 3))
 
-        runAfterServicesAreAdded(services.size) {
-            connectDevice(device) {
-                var offset = 0
-                values.forEachIndexed { index, value ->
-                    serverAdapter.callback.onCharacteristicWriteRequest(
-                        device, /*requestId=*/index + 1, writeCharacteristic.fwkCharacteristic,
-                        /*preparedWrite=*/true, /*responseNeeded=*/false,
-                        offset, value
-                    )
-                    offset += value.size
-                }
-                serverAdapter.callback.onExecuteWrite(device, /*requestId=*/values.size + 1, true)
-            }
-        }
         serverAdapter.onCloseGattServerListener =
             StubServerFrameworkAdapter.OnCloseGattServerListener {
                 closed.complete(Unit)
             }
 
-        launch {
-            bluetoothLe.openGattServer(services).collect {
+        bluetoothLe.openGattServer(services)
+            .onOpened {
+                connectDevice(device) {
+                    var offset = 0
+                    values.forEachIndexed { index, value ->
+                        serverAdapter.callback.onCharacteristicWriteRequest(
+                            device, /*requestId=*/index + 1, writeCharacteristic.fwkCharacteristic,
+                            /*preparedWrite=*/true, /*responseNeeded=*/false,
+                            offset, value
+                        )
+                        offset += value.size
+                    }
+                    serverAdapter.callback.onExecuteWrite(
+                        device,
+                        /*requestId=*/values.size + 1,
+                        /*execute=*/ true
+                    )
+                }
+            }
+            .onClosed {
+                assertTrue(closed.isCompleted)
+            }
+            .first().let {
                 it.accept {
                     when (val request = requests.first()) {
                         is GattServerRequest.WriteCharacteristics -> {
@@ -639,24 +638,8 @@
 
                         else -> fail("unexpected request")
                     }
-                    // Close the server
-                    [email protected]()
                 }
             }
-        }.join()
-
-        assertTrue(closed.isCompleted)
-    }
-
-    private fun runAfterServicesAreAdded(countServices: Int, block: suspend () -> R) {
-        var waitCount = countServices
-        serverAdapter.onAddServiceListener = StubServerFrameworkAdapter.OnAddServiceListener {
-            if (--waitCount == 0) {
-                runBlocking {
-                    block()
-                }
-            }
-        }
     }
 
     private fun connectDevice(device: FwkDevice, block: () -> R): R {
@@ -685,6 +668,10 @@
         var onNotifyCharacteristicChangedListener: OnNotifyCharacteristicChangedListener? = null
         var onSendResponseListener: OnSendResponseListener? = null
 
+        override fun isOpened(): Boolean {
+            return baseAdapter.isOpened()
+        }
+
         override fun openGattServer(context: Context, fwkCallback: BluetoothGattServerCallback) {
             baseAdapter.openGattServer(context, fwkCallback)
             onOpenGattServerListener?.onOpenGattServer()
@@ -701,6 +688,7 @@
 
         override fun addService(fwkService: FwkService) {
             baseAdapter.addService(fwkService)
+            callback.onServiceAdded(GATT_SUCCESS, fwkService)
             onAddServiceListener?.onAddService(fwkService)
         }
 
@@ -710,17 +698,14 @@
             confirm: Boolean,
             value: ByteArray
         ): Int? {
-            baseAdapter.notifyCharacteristicChanged(fwkDevice, fwkCharacteristic, confirm, value)
-                .let {
-                    onNotifyCharacteristicChangedListener
-                        ?.onNotifyCharacteristicChanged(
-                            fwkDevice,
-                            fwkCharacteristic,
-                            confirm,
-                            value
-                        )
-                    return it
-                }
+            onNotifyCharacteristicChangedListener
+                ?.onNotifyCharacteristicChanged(
+                    fwkDevice,
+                    fwkCharacteristic,
+                    confirm,
+                    value
+                )
+            return FwkBluetoothStatusCodes.SUCCESS
         }
 
         override fun sendResponse(
diff --git a/bluetooth/bluetooth/api/current.txt b/bluetooth/bluetooth/api/current.txt
index 3c9bcc3..dd2a2de 100644
--- a/bluetooth/bluetooth/api/current.txt
+++ b/bluetooth/bluetooth/api/current.txt
@@ -124,7 +124,7 @@
   }
 
   public interface GattServerConnectFlow extends kotlinx.coroutines.flow.Flow {
-    method public void updateServices(java.util.List services);
+    method public suspend Object? updateServices(java.util.List services, kotlin.coroutines.Continuation);
   }
 
   public final class GattServerConnectRequest {
diff --git a/bluetooth/bluetooth/api/restricted_current.txt b/bluetooth/bluetooth/api/restricted_current.txt
index 3c9bcc3..dd2a2de 100644
--- a/bluetooth/bluetooth/api/restricted_current.txt
+++ b/bluetooth/bluetooth/api/restricted_current.txt
@@ -124,7 +124,7 @@
   }
 
   public interface GattServerConnectFlow extends kotlinx.coroutines.flow.Flow {
-    method public void updateServices(java.util.List services);
+    method public suspend Object? updateServices(java.util.List services, kotlin.coroutines.Continuation);
   }
 
   public final class GattServerConnectRequest {
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/GattServer.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/GattServer.kt
index 3fb443d..2f0dbe2 100644
--- a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/GattServer.kt
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/GattServer.kt
@@ -72,6 +72,7 @@
 
     interface FrameworkAdapter {
         var fwkGattServer: FwkBluetoothGattServer?
+        fun isOpened(): Boolean
         fun openGattServer(context: Context, fwkCallback: FwkBluetoothGattServerCallback)
         fun closeGattServer()
         fun clearServices()
@@ -126,10 +127,28 @@
         private val sessions = mutableMapOf()
         private val notifyMutex = Mutex()
         private var notifyJob: CompletableDeferred? = null
+        private val servicesMutex = Mutex()
+        private var serviceCallbackChannel: Channel? = null
 
-        override fun updateServices(services: List) {
-            fwkAdapter.clearServices()
-            services.forEach { fwkAdapter.addService(it.fwkService) }
+        private var onOpened: (suspend () -> Unit)? = null
+        private var onClosed: (suspend () -> Unit)? = null
+
+        override suspend fun updateServices(services: List) {
+            if (!fwkAdapter.isOpened()) throw IllegalStateException("GATT server is not opened")
+            servicesMutex.withLock {
+                fwkAdapter.clearServices()
+                addServices(services)
+            }
+        }
+
+        override fun onOpened(action: suspend () -> Unit): GattServerConnectFlow {
+            onOpened = action
+            return this
+        }
+
+        override fun onClosed(action: suspend () -> Unit): GattServerConnectFlow {
+            onClosed = action
+            return this
         }
 
         override suspend fun collectSafely(collector: FlowCollector) {
@@ -155,6 +174,10 @@
                         }
                     }
 
+                    override fun onServiceAdded(status: Int, service: FwkBluetoothGattService) {
+                        serviceCallbackChannel?.trySend(service)
+                    }
+
                     override fun onCharacteristicReadRequest(
                         fwkDevice: FwkBluetoothDevice,
                         requestId: Int,
@@ -294,16 +317,32 @@
                     }
                 }
                 fwkAdapter.openGattServer(context, callback)
-                services.forEach { fwkAdapter.addService(it.fwkService) }
+                addServices(services)
+
+                onOpened?.invoke()
 
                 awaitClose {
                     fwkAdapter.closeGattServer()
                 }
+                onClosed?.invoke()
             }
 
             connectRequests.collect { collector.emit(it) }
         }
 
+        private suspend fun addServices(services: List) {
+            // Capacity = 1 allows getting callback before it's caught
+            serviceCallbackChannel = Channel(1)
+            services.forEach {
+                fwkAdapter.addService(it.fwkService)
+                val addedService = serviceCallbackChannel?.receive()
+                if (addedService != it.fwkService) {
+                    throw BluetoothException(BluetoothException.ERROR_UNKNOWN)
+                }
+            }
+            serviceCallbackChannel = null
+        }
+
         private fun addSession(fwkDevice: FwkBluetoothDevice): GattServer.Session {
             return Session(BluetoothDevice(fwkDevice)).apply {
                 sessions[fwkDevice] = this
@@ -455,11 +494,15 @@
     private open class FrameworkAdapterBase : FrameworkAdapter {
 
         override var fwkGattServer: FwkBluetoothGattServer? = null
-        private val isOpen = AtomicBoolean(false)
+        private val isOpened = AtomicBoolean(false)
+
+        override fun isOpened(): Boolean {
+            return isOpened.get()
+        }
 
         @SuppressLint("MissingPermission")
         override fun openGattServer(context: Context, fwkCallback: FwkBluetoothGattServerCallback) {
-            if (!isOpen.compareAndSet(false, true))
+            if (!isOpened.compareAndSet(false, true))
                 throw IllegalStateException("GATT server is already opened")
             val bluetoothManager =
                 context.getSystemService(Context.BLUETOOTH_SERVICE) as FwkBluetoothManager?
@@ -468,7 +511,7 @@
 
         @SuppressLint("MissingPermission")
         override fun closeGattServer() {
-            if (!isOpen.compareAndSet(true, false))
+            if (!isOpened.compareAndSet(true, false))
                 throw IllegalStateException("GATT server is already closed")
             fwkGattServer?.close()
         }
@@ -575,6 +618,21 @@
     }
 }
 
+/**
+ * A flow of [GattServerConnectRequest] returned by calling [BluetoothLe.openGattServer].
+ */
 interface GattServerConnectFlow : Flow {
-    fun updateServices(services: List)
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    fun onOpened(action: suspend () -> Unit): GattServerConnectFlow
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    fun onClosed(action: suspend () -> Unit): GattServerConnectFlow
+    /**
+     * Updates the services provided by the opened GATT server.
+     *
+     * @param services a new list of services that should be provided
+     *
+     * @throws IllegalStateException if it's called before the server is opened.
+     */
+    suspend fun updateServices(services: List)
 }
diff --git a/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/PathEasingTest.kt b/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/PathEasingTest.kt
index 608240f..0f8a79b 100644
--- a/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/PathEasingTest.kt
+++ b/compose/animation/animation-core/src/androidInstrumentedTest/kotlin/androidx/compose/animation/core/PathEasingTest.kt
@@ -21,6 +21,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
+import java.lang.IllegalStateException
 import org.junit.Assert.assertEquals
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -30,42 +31,140 @@
 class PathEasingTest {
     @Test
     fun pathEasing_Emphasized_BoundsCheck() {
-        val path = Path()
-        path.moveTo(0f, 0f)
-        path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f)
-        path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f)
+        val path = Path().apply {
+            moveTo(0f, 0f)
+            cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f)
+            cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f)
+        }
 
         val easing = PathEasing(path)
         assertThat(easing.transform(0f)).isZero()
         assertThat(easing.transform(1f)).isEqualTo(1f)
 
-        assertEquals(0.77283f, easing.transform(0.25f), 0.0001f)
-        assertEquals(0.95061f, easing.transform(0.5f), 0.0001f)
-        assertEquals(0.99139f, easing.transform(0.75f), 0.0001f)
+        assertEquals(0.77283f, easing.transform(0.25f), 1e-4f)
+        assertEquals(0.95061f, easing.transform(0.50f), 1e-4f)
+        assertEquals(0.99139f, easing.transform(0.75f), 1e-4f)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun pathEasing_EmptyPath_InvalidPath() {
+        val emptyPath = Path()
+        PathEasing(emptyPath).transform(0.5f)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun pathEasing_DoesNotStartAtZero() {
+        val path = Path().apply {
+            moveTo(0.1f, 0.0f)
+            lineTo(1.0f, 1.0f)
+        }
+        PathEasing(path).transform(0.5f)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun pathEasing_DoesNotEndAtOne() {
+        val path = Path().apply {
+            lineTo(0.9f, 1.0f)
+        }
+        PathEasing(path).transform(0.5f)
     }
 
     @Test
-    fun pathEasing_CheckIncreasingXOverTime() {
-        val path = Path()
-        path.moveTo(0f, 0f)
-        path.quadraticTo(0f, 1.65f, 1f, -0.6f)
+    fun pathEasing_CompareToCubicEasing() {
+        val cubicEasing = CubicBezierEasing(0.4f, 0.0f, 0.2f, 1.0f)
+        val path = Path().apply {
+            cubicTo(0.4f, 0.0f, 0.2f, 1.0f, 1.0f, 1.0f)
+        }
 
         val easing = PathEasing(path)
-        assertThat(easing.transform(0f)).isZero()
-        assertThat(easing.transform(1f)).isEqualTo(1f)
+        for (i in 0..256) {
+            val fraction = i / 256f
+            assertEquals(cubicEasing.transform(fraction), easing.transform(fraction), 1e-6f)
+        }
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun pathEasing_NonContinuousPath() {
+        val path = Path().apply {
+            moveTo(0.00f, 0.10f)
+            lineTo(0.25f, 0.10f)
+            // Gap from 0.25 to 0.50
+            moveTo(0.50f, 0.40f)
+            lineTo(0.75f, 0.40f)
+            moveTo(0.75f, 1.00f)
+            lineTo(1.00f, 1.00f)
+        }
+
+        val easing = PathEasing(path)
+        assertEquals(0.1f, easing.transform(0.2f))
+        // Crash
+        easing.transform(0.4f)
     }
 
     @Test(expected = IllegalArgumentException::class)
-    fun pathEasing_CheckIncreasingXOverTime_InvalidPath() {
-        val path = Path()
-        path.addOval(Rect(0f, 0f, 1f, 1f))
+    fun pathEasing_ClosedPath() {
+        val path = Path().apply {
+            addOval(Rect(0f, 0f, 1f, 1f))
+        }
 
-        PathEasing(path)
+        PathEasing(path).transform(0.5f)
     }
 
-    @Test(expected = IllegalArgumentException::class)
-    fun pathEasing_NoPathProvided_ThrowsIllegalArgument() {
-        val emptyPath = Path()
-        PathEasing(emptyPath)
+    @Test
+    fun pathEasing_Overlapping_Curves() {
+        val path = Path().apply {
+            moveTo(0.00f, 0.10f)
+            lineTo(0.25f, 0.10f)
+            moveTo(0.10f, 0.30f) // Overlaps with the previous line
+            lineTo(0.60f, 0.30f) // and the next line
+            moveTo(0.50f, 0.40f)
+            lineTo(0.75f, 0.40f)
+            moveTo(0.75f, 1.00f)
+            lineTo(1.00f, 1.00f)
+        }
+
+        val easing = PathEasing(path)
+
+        // We don't specify which overlapping curve will be evaluated first
+        assertThat(easing.transform(0.2f)).isAnyOf(0.10f, 0.30f)
+    }
+
+    @Test
+    fun pathEasing_QuadTo() {
+        val path = Path().apply {
+            quadraticTo(1.0f, 0.0f, 1.0f, 1.0f)
+        }
+
+        val easing = PathEasing(path)
+        var previousFraction = -Float.MAX_VALUE
+        for (i in 0..256) {
+            val fraction = i / 256f
+            val newFraction = easing.transform(fraction)
+
+            assertThat(newFraction).isAtLeast(0.0f)
+            assertThat(newFraction).isGreaterThan(previousFraction)
+
+            previousFraction = newFraction
+        }
+    }
+
+    @Test
+    fun pathEasing_QuadTo_OneToZero() {
+        val path = Path().apply {
+            moveTo(1.0f, 1.0f)
+            quadraticTo(1.0f, 0.0f, 0.0f, 0.0f)
+        }
+
+        val easing = PathEasing(path)
+        var previousFraction = -Float.MAX_VALUE
+        for (i in 0..256) {
+            val fraction = i / 256f
+            val newFraction = easing.transform(fraction)
+
+            assertThat(newFraction).isAtLeast(0.0f)
+            assertThat(newFraction).isGreaterThan(previousFraction)
+
+            previousFraction = newFraction
+        }
     }
 }
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Bezier.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Bezier.kt
index 98fe46d..bc16918 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Bezier.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Bezier.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.animation.core
 
+import androidx.collection.FloatFloatPair
+import androidx.compose.ui.graphics.PathSegment
 import kotlin.math.abs
 import kotlin.math.acos
 import kotlin.math.cbrt
@@ -29,30 +31,231 @@
 private const val FloatEpsilon = 1e-7f
 
 /**
- * Evaluates a cubic Bézier curve at position [t] along the curve and returns
- * the Y coordinate at that position. The curve is defined by the start
- * point (0, 0), the end point (0, 0) and two control points of respective
- * Y coordinates [p1y] and [p2y].
+ * Evaluate the specified [segment] at position [t] and returns the X
+ * coordinate of the segment's curve at that position.
+ */
+private fun evaluateX(
+    segment: PathSegment,
+    t: Float
+): Float {
+    val points = segment.points
+
+    return when (segment.type) {
+        PathSegment.Type.Move -> points[0]
+
+        PathSegment.Type.Line -> {
+            evaluateLine(
+                points[0],
+                points[2],
+                t
+            )
+        }
+
+        PathSegment.Type.Quadratic -> {
+            evaluateQuadratic(
+                points[0],
+                points[2],
+                points[4],
+                t
+            )
+        }
+
+        // We convert all conics to cubics, won't happen
+        PathSegment.Type.Conic -> Float.NaN
+
+        PathSegment.Type.Cubic -> {
+            evaluateCubic(
+                points[0],
+                points[2],
+                points[4],
+                points[6],
+                t
+            )
+        }
+
+        PathSegment.Type.Close -> Float.NaN
+        PathSegment.Type.Done -> Float.NaN
+    }
+}
+
+/**
+ * Evaluate the specified [segment] at position [t] and returns the Y
+ * coordinate of the segment's curve at that position.
+ */
+internal fun evaluateY(
+    segment: PathSegment,
+    t: Float
+): Float {
+    val points = segment.points
+
+    return when (segment.type) {
+        PathSegment.Type.Move -> points[1]
+
+        PathSegment.Type.Line -> {
+            evaluateLine(
+                points[1],
+                points[3],
+                t
+            )
+        }
+
+        PathSegment.Type.Quadratic -> {
+            evaluateQuadratic(
+                points[1],
+                points[3],
+                points[5],
+                t
+            )
+        }
+
+        // We convert all conics to cubics, won't happen
+        PathSegment.Type.Conic -> Float.NaN
+
+        PathSegment.Type.Cubic -> {
+            evaluateCubic(
+                points[1],
+                points[3],
+                points[5],
+                points[7],
+                t
+            )
+        }
+
+        PathSegment.Type.Close -> Float.NaN
+        PathSegment.Type.Done -> Float.NaN
+    }
+}
+
+private fun evaluateLine(
+    p0y: Float,
+    p1y: Float,
+    t: Float
+) = (p1y - p0y) * t + p0y
+
+private fun evaluateQuadratic(
+    p0: Float,
+    p1: Float,
+    p2: Float,
+    t: Float
+): Float {
+    val by = 2.0 * (p1 - p0)
+    val ay = p2 - 2.0 * p1 + p0
+    return ((ay * t + by) * t + p0).toFloat()
+}
+
+private fun evaluateCubic(
+    p0: Float,
+    p1: Float,
+    p2: Float,
+    p3: Float,
+    t: Float
+): Float {
+    val a = p3 + 3.0 * (p1 - p2) - p0
+    val b = 3.0 * (p2 - 2.0 * p1 + p0)
+    val c = 3.0 * (p1 - p0)
+    return (((a * t + b) * t + c) * t + p0).toFloat()
+}
+
+/**
+ * Evaluates a cubic Bézier curve at position [t] along the curve. The curve is
+ * defined by the start point (0, 0), the end point (0, 0) and two control points
+ * of respective coordinates [p1] and [p2].
  */
 @Suppress("UnnecessaryVariable")
 internal fun evaluateCubic(
-    p1y: Float,
-    p2y: Float,
+    p1: Float,
+    p2: Float,
     t: Float
 ): Float {
-    val a = 1.0 / 3.0 + (p1y - p2y)
-    val b = (p2y - 2.0 * p1y)
-    val c = p1y
+    val a = 1.0 / 3.0 + (p1 - p2)
+    val b = (p2 - 2.0 * p1)
+    val c = p1
     return 3.0f * (((a * t + b) * t + c) * t).toFloat()
 }
 
 /**
- * Finds the first real root of a cubic Bézier curve. To find the roots, only the X
- * coordinates of the four points are required:
- * - [p0]: x coordinate of the start point
- * - [p1]: x coordinate of the first control point
- * - [p2]: x coordinate of the second control point
- * - [p3]: x coordinate of the end point
+ * Finds the first real root of the specified [segment].
+ * If no root can be found, this method returns [Float.NaN].
+ */
+internal fun findFirstRoot(
+    segment: PathSegment,
+    fraction: Float
+): Float {
+    val points = segment.points
+    return when (segment.type) {
+        PathSegment.Type.Move -> Float.NaN
+
+        PathSegment.Type.Line -> {
+            findFirstLineRoot(
+                points[0] - fraction,
+                points[2] - fraction,
+            )
+        }
+
+        PathSegment.Type.Quadratic -> findFirstQuadraticRoot(
+            points[0] - fraction,
+            points[2] - fraction,
+            points[4] - fraction
+        )
+
+        // We convert all conics to cubics, won't happen
+        PathSegment.Type.Conic -> Float.NaN
+
+        PathSegment.Type.Cubic -> findFirstCubicRoot(
+            points[0] - fraction,
+            points[2] - fraction,
+            points[4] - fraction,
+            points[6] - fraction
+        )
+
+        PathSegment.Type.Close -> Float.NaN
+        PathSegment.Type.Done -> Float.NaN
+    }
+}
+
+@Suppress("NOTHING_TO_INLINE")
+private inline fun findFirstLineRoot(p0: Float, p1: Float) =
+    clampValidRootInUnitRange(-p0 / (p1 - p0))
+
+/**
+ * Finds the first real root of a quadratic Bézier curve:
+ * - [p0]: coordinate of the start point
+ * - [p1]: coordinate of the control point
+ * - [p2]: coordinate of the end point
+ *
+ * If no root can be found, this method returns [Float.NaN].
+ */
+private fun findFirstQuadraticRoot(
+    p0: Float,
+    p1: Float,
+    p2: Float
+): Float {
+    val a = p0.toDouble()
+    val b = p1.toDouble()
+    val c = p2.toDouble()
+    val d = a - 2.0 * b + c
+
+    if (d != 0.0) {
+        val v1 = -sqrt(b * b - a * c)
+        val v2 = -a + b
+
+        val root = clampValidRootInUnitRange((-(v1 + v2) / d).toFloat())
+        if (!root.isNaN()) return root
+
+        return clampValidRootInUnitRange(((v1 - v2) / d).toFloat())
+    } else if (b != c) {
+        return clampValidRootInUnitRange(((2.0 * b - c) / (2.0 * b - 2.0 * c)).toFloat())
+    }
+
+    return Float.NaN
+}
+
+/**
+ * Finds the first real root of a cubic Bézier curve:
+ * - [p0]: coordinate of the start point
+ * - [p1]: coordinate of the first control point
+ * - [p2]: coordinate of the second control point
+ * - [p3]: coordinate of the end point
  *
  * If no root can be found, this method returns [Float.NaN].
  */
@@ -136,141 +339,137 @@
 }
 
 /**
- * Finds the real roots of a cubic Bézier curve. To find the roots, only the X
+ * Finds the real root of a line defined by the X coordinates of its start ([p0])
+ * and end ([p1]) points. The root, if any, is written in the [roots] array at
+ * [index]. Returns 1 if a root was found, 0 otherwise.
+ */
+@Suppress("NOTHING_TO_INLINE")
+private inline fun findLineRoot(p0: Float, p1: Float, roots: FloatArray, index: Int = 0) =
+    writeValidRootInUnitRange(-p0 / (p1 - p0), roots, index)
+
+/**
+ * Finds the real roots of a quadratic Bézier curve. To find the roots, only the X
  * coordinates of the four points are required:
  * - [p0]: x coordinate of the start point
- * - [p1]: x coordinate of the first control point
- * - [p2]: x coordinate of the second control point
- * - [p3]: x coordinate of the end point
+ * - [p1]: x coordinate of the control point
+ * - [p2]: x coordinate of the end point
  *
- * This function returns the number of roots written in the [roots] array
- * starting at [index]. The number of roots can be 0, 1, 2, or 3. If the
- * function returns 0, no root was found and the cubic curve does not have
- * a numerical solution and should be considered invalid.
+ * Any root found is written in the [roots] array, starting at [index]. The
+ * function returns the number of roots found and written to the array.
  */
-internal fun findCubicRoots(
+private fun findQuadraticRoots(
     p0: Float,
     p1: Float,
     p2: Float,
-    p3: Float,
     roots: FloatArray,
     index: Int = 0
 ): Int {
-    // This function implements Cardano's algorithm as described in "A Primer on Bézier Curves":
-    // https://pomax.github.io/bezierinfo/#yforx
-    //
-    // The math used to find the roots is explained in "Solving the Cubic Equation":
-    // http://www.trans4mind.com/personal_development/mathematics/polynomials/cubicAlgebra.htm
-
-    var a = 3.0 * p0 - 6.0 * p1 + 3.0 * p2
-    var b = -3.0 * p0 + 3.0 * p1
-    var c = p0.toDouble()
-    val d = -p0 + 3.0 * p1 - 3.0 * p2 + p3
+    val a = p0.toDouble()
+    val b = p1.toDouble()
+    val c = p2.toDouble()
+    val d = a - 2.0 * b + c
 
     var rootCount = 0
 
-    // Not a cubic
-    if (d.closeTo(0.0)) {
-        // Not a quadratic
-        if (a.closeTo(0.0)) {
-            // No solutions
-            if (b.closeTo(0.0)) {
-                return 0
-            }
-            return writeValidRootInUnitRange((-c / b).toFloat(), roots, index)
-        } else {
-            val q = sqrt(b * b - 4.0 * a * c)
-            val a2 = 2.0 * a
+    if (d != 0.0) {
+        val v1 = -sqrt(b * b - a * c)
+        val v2 = -a + b
 
-            rootCount += writeValidRootInUnitRange(
-                ((q - b) / a2).toFloat(), roots, index
-            )
-            rootCount += writeValidRootInUnitRange(
-                ((-b - q) / a2).toFloat(), roots, index + rootCount
-            )
-            return rootCount
+        rootCount += writeValidRootInUnitRange(
+            (-(v1 + v2) / d).toFloat(), roots, index
+        )
+        rootCount += writeValidRootInUnitRange(
+            ((v1 - v2) / d).toFloat(), roots, index + rootCount
+        )
+    } else if (b != c) {
+        rootCount += writeValidRootInUnitRange(
+            ((2.0 * b - c) / (2.0 * b - 2.0 * c)).toFloat(), roots, index
+        )
+    }
+
+    return rootCount
+}
+
+/**
+ * Finds the roots of the derivative of the curve described by [segment].
+ * The roots, if any, are written in the [roots] array starting at [index].
+ * The function returns the number of roots founds and written into the array.
+ * The [roots] array must be able to hold at least 5 floats starting at [index].
+ */
+private fun findDerivativeRoots(
+    segment: PathSegment,
+    roots: FloatArray,
+    index: Int = 0,
+): Int {
+    val points = segment.points
+    return when (segment.type) {
+        PathSegment.Type.Move -> 0
+
+        PathSegment.Type.Line -> 0
+
+        PathSegment.Type.Quadratic -> {
+            // Line derivative of a quadratic function
+            // We do the computation inline to avoid using arrays of other data
+            // structures to return the result
+            val d0 = 2 * (points[2] - points[0])
+            val d1 = 2 * (points[4] - points[2])
+            findLineRoot(d0, d1, roots, index)
         }
+
+        // We convert all conics to cubics, won't happen
+        PathSegment.Type.Conic -> 0
+
+        PathSegment.Type.Cubic -> {
+            // Quadratic derivative of a cubic function
+            // We do the computation inline to avoid using arrays of other data
+            // structures to return the result
+            val d0 = 3 * (points[2] - points[0])
+            val d1 = 3 * (points[4] - points[2])
+            val d2 = 3 * (points[6] - points[4])
+            val count = findQuadraticRoots(d0, d1, d2, roots, index)
+
+            // Compute the second derivative as a line
+            val dd0 = 2 * (d1 - d0)
+            val dd1 = 2 * (d2 - d1)
+            count + findLineRoot(dd0, dd1, roots, index + count)
+        }
+
+        PathSegment.Type.Close -> 0
+        PathSegment.Type.Done -> 0
+    }
+}
+
+/**
+ * Computes the horizontal bounds of the specified [segment] and returns
+ * a pair of floats containing the lowest bound as the first value, and
+ * the highest bound as the second value.
+ *
+ * The [roots] array is used as a scratch array and must be able to hold
+ * at least 5 floats.
+ */
+internal fun computeHorizontalBounds(
+    segment: PathSegment,
+    roots: FloatArray,
+    index: Int = 0
+): FloatFloatPair {
+    val count = findDerivativeRoots(segment, roots, index)
+    var minX = min(segment.startX, segment.endX)
+    var maxX = max(segment.startX, segment.endX)
+
+    for (i in 0 until count) {
+        val t = roots[i]
+        val x = evaluateX(segment, t)
+        minX = min(minX, x)
+        maxX = max(maxX, x)
     }
 
-    a /= d
-    b /= d
-    c /= d
-
-    val o = (3.0 * b - a * a) / 3.0
-    val o3 = o / 3.0
-    val q = (2.0 * a * a * a - 9.0 * a * b + 27.0 * c) / 27.0
-    val q2 = q / 2.0
-    val discriminant = q2 * q2 + o3 * o3 * o3
-
-    if (discriminant < 0.0) {
-        val mp3 = -o / 3.0
-        val mp33 = mp3 * mp3 * mp3
-        val r = sqrt(mp33)
-        val t = -q / (2.0 * r)
-        val cosPhi = min(1.0, max(-1.0, t))
-        val phi = acos(cosPhi)
-        val t1 = 2.0 * cbrt(r)
-
-        rootCount += writeValidRootInUnitRange(
-            (t1 * cos(phi / 3.0) - a / 3.0).toFloat(), roots, index
-        )
-        rootCount += writeValidRootInUnitRange(
-            (t1 * cos((phi + Tau) / 3.0) - a / 3.0).toFloat(),
-            roots,
-            index + rootCount
-        )
-        rootCount += writeValidRootInUnitRange(
-            (t1 * cos((phi + 2.0 * Tau) / 3.0) - a / 3.0).toFloat(),
-            roots,
-            index + rootCount
-        )
-        return rootCount
-    } else if (discriminant == 0.0) { // TODO: closeTo(0.0)?
-        val u1 = if (q2 < 0.0) cbrt(-q2) else -cbrt(q2)
-
-        rootCount += writeValidRootInUnitRange(
-            (2.0 * u1 - a / 3.0).toFloat(),
-            roots,
-            index
-        )
-        rootCount += writeValidRootInUnitRange(
-            (-u1 - a / 3.0).toFloat(),
-            roots,
-            index + rootCount
-        )
-        return rootCount
-    }
-
-    val sd = sqrt(discriminant)
-    val u1 = cbrt(-q2 + sd)
-    val v1 = cbrt(q2 + sd)
-
-    return writeValidRootInUnitRange((u1 - v1 - a / 3.0).toFloat(), roots, index)
+    return FloatFloatPair(minX, maxX)
 }
 
 @Suppress("NOTHING_TO_INLINE")
 private inline fun Double.closeTo(b: Double, epsilon: Double = Epsilon) = abs(this - b) < epsilon
 
 /**
- * Writes the root [r] in the [roots] array at the specified [index]. If [r]
- * is outside the [0..1] range, [Float.NaN] is written instead. To account for
- * numerical imprecision in computations, values in the [-FloatEpsilon..1+FloatEpsilon]
- * range are considered to be in the [0..1] range and clamped appropriately.
- */
-@Suppress("NOTHING_TO_INLINE")
-private inline fun writeValidRootInUnitRange(r: Float, roots: FloatArray, index: Int): Int {
-    val v = if (r < 0.0f) {
-        if (r >= -FloatEpsilon) 0.0f else Float.NaN
-    } else if (r > 1.0f) {
-        if (r <= 1.0f + FloatEpsilon) 1.0f else Float.NaN
-    } else {
-        r
-    }
-    roots[index] = v
-    return if (v.isNaN()) 0 else 1
-}
-
-/**
  * Returns [r] if it's in the [0..1] range, and [Float.NaN] otherwise. To account
  * for numerical imprecision in computations, values in the [-FloatEpsilon..1+FloatEpsilon]
  * range are considered to be in the [0..1] range and clamped appropriately.
@@ -283,3 +482,30 @@
 } else {
     r
 }
+
+/**
+ * Writes [r] in the [roots] array at [index], if it's in the [0..1] range. To account
+ * for numerical imprecision in computations, values in the [-FloatEpsilon..1+FloatEpsilon]
+ * range are considered to be in the [0..1] range and clamped appropriately. Returns 0 if
+ * no value was written, 1 otherwise.
+ */
+@Suppress("NOTHING_TO_INLINE")
+private inline fun writeValidRootInUnitRange(r: Float, roots: FloatArray, index: Int): Int {
+    val v = clampValidRootInUnitRange(r)
+    roots[index] = v
+    return if (v.isNaN()) 0 else 1
+}
+
+private inline val PathSegment.startX: Float
+    get() = points[0]
+
+private val PathSegment.endX: Float
+    get() = points[when (type) {
+        PathSegment.Type.Move -> 0
+        PathSegment.Type.Line -> 2
+        PathSegment.Type.Quadratic -> 4
+        PathSegment.Type.Conic -> 4
+        PathSegment.Type.Cubic -> 6
+        PathSegment.Type.Close -> 0
+        PathSegment.Type.Done -> 0
+    }]
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt
index f3baa1b..b1081ad 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Easing.kt
@@ -71,7 +71,11 @@
  *
  * The [CubicBezierEasing] class implements third-order Bézier curves.
  *
- * This is equivalent to the Android `PathInterpolator`
+ * This is equivalent to the Android `PathInterpolator` when a single cubic Bézier
+ * curve is specified.
+ *
+ * Note: [CubicBezierEasing] instances are stateless and can be used concurrently
+ * from multiple threads.
  *
  * Rather than creating a new instance, consider using one of the common
  * cubic [Easing]s:
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/IntervalTree.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/IntervalTree.kt
new file mode 100644
index 0000000..02b67d9
--- /dev/null
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/IntervalTree.kt
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation.core
+import kotlin.math.max
+import kotlin.math.min
+
+// TODO: We should probably move this to androidx.collection
+
+/**
+ * Interval in an [IntervalTree]. The interval is defined between a [start] and an [end]
+ * coordinates, whose meanings are defined by the caller. An interval can also hold
+ * arbitrary [data] to be used to looking at the result of queries with
+ * [IntervalTree.findOverlaps].
+ */
+internal class Interval(val start: Float, val end: Float, val data: T? = null) {
+    /**
+     * Returns trues if this interval overlaps with another interval.
+     */
+    fun overlaps(other: Interval) = start <= other.end && end >= other.start
+
+    /**
+     * Returns trues if this interval overlaps with the interval defined by [start]
+     * and [end]. [start] must be less than or equal to [end].
+     */
+    fun overlaps(start: Float, end: Float) = this.start <= end && this.end >= start
+
+    /**
+     * Returns true if this interval contains [value].
+     */
+    operator fun contains(value: Float) = value in start..end
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || this::class != other::class) return false
+
+        other as Interval<*>
+
+        if (start != other.start) return false
+        if (end != other.end) return false
+        if (data != other.data) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = start.hashCode()
+        result = 31 * result + end.hashCode()
+        result = 31 * result + (data?.hashCode() ?: 0)
+        return result
+    }
+
+    override fun toString(): String {
+        return "Interval(start=$start, end=$end, data=$data)"
+    }
+}
+
+internal val EmptyInterval: Interval = Interval(Float.MAX_VALUE, Float.MIN_VALUE, null)
+
+/**
+ * An interval tree holds a list of intervals and allows for fast queries of intervals
+ * that overlap any given interval. This can be used for instance to perform fast spatial
+ * queries like finding all the segments in a path that overlap with a given vertical
+ * interval.
+ */
+internal class IntervalTree {
+    // Note: this interval tree is implemented as a binary red/black tree that gets
+    // re-balanced on updates. There's nothing notable about this particular data
+    // structure beyond what can be found in various descriptions of binary search
+    // trees and red/black trees
+
+    private val terminator = Node(
+        Interval(Float.MAX_VALUE, Float.MIN_VALUE, null),
+        TreeColor.Black
+    )
+    private var root = terminator
+
+    /**
+     * Clears this tree and prepares it for reuse. After calling [clear], any call to
+     * [findOverlaps] returns false.
+     */
+    fun clear() {
+        root = terminator
+    }
+
+    /**
+     * Finds the first interval that overlaps with the specified [interval]. If no overlap can
+     * be found, return [EmptyInterval].
+     */
+    fun findFirstOverlap(interval: ClosedFloatingPointRange) =
+        findFirstOverlap(interval.start, interval.endInclusive)
+
+    /**
+     * Finds the first interval that overlaps with the interval defined by [start] and [end].
+     * If no overlap can be found, return [EmptyInterval]. [start] *must* be lesser than or
+     * equal to [end].
+     */
+    fun findFirstOverlap(
+        start: Float,
+        end: Float = start
+    ): Interval {
+        if (root !== terminator) {
+            return findFirstOverlap(root, start, end)
+        }
+        @Suppress("UNCHECKED_CAST")
+        return EmptyInterval as Interval
+    }
+
+    private fun findFirstOverlap(
+        node: Node,
+        start: Float,
+        end: Float
+    ): Interval {
+        if (node.interval.overlaps(start, end)) return node.interval
+        if (node.left !== terminator && node.left.max >= start) {
+            return findFirstOverlap(node.left, start, end)
+        }
+        if (node.right !== terminator && node.right.min <= end) {
+            return findFirstOverlap(node.right, start, end)
+        }
+        @Suppress("UNCHECKED_CAST")
+        return EmptyInterval as Interval
+    }
+
+    /**
+     * Finds all the intervals that overlap with the specified [interval]. If [results]
+     * is specified, [results] is returned, otherwise a new [MutableList] is returned.
+     */
+    fun findOverlaps(
+        interval: ClosedFloatingPointRange,
+        results: MutableList> = mutableListOf()
+    ) = findOverlaps(interval.start, interval.endInclusive, results)
+
+    /**
+     * Finds all the intervals that overlap with the interval defined by [start] and [end].
+     * [start] *must* be lesser than or equal to [end]. If [results] is specified, [results]
+     * is returned, otherwise a new [MutableList] is returned.
+     */
+    fun findOverlaps(
+        start: Float,
+        end: Float = start,
+        results: MutableList> = mutableListOf()
+    ): MutableList> {
+        if (root !== terminator) {
+            findOverlaps(root, start, end, results)
+        }
+        return results
+    }
+
+    private fun findOverlaps(
+        node: Node,
+        start: Float,
+        end: Float,
+        results: MutableList>
+    ) {
+        if (node.interval.overlaps(start, end)) results.add(node.interval)
+        if (node.left !== terminator && node.left.max >= start) {
+            findOverlaps(node.left, start, end, results)
+        }
+        if (node.right !== terminator && node.right.min <= end) {
+            findOverlaps(node.right, start, end, results)
+        }
+    }
+
+    /**
+     * Returns true if [value] is inside any of the intervals in this tree.
+     */
+    operator fun contains(value: Float) = findFirstOverlap(value, value) !== EmptyInterval
+
+    /**
+     * Returns true if the specified [interval] overlaps with any of the intervals
+     * in this tree.
+     */
+    operator fun contains(interval: ClosedFloatingPointRange) =
+        findFirstOverlap(interval.start, interval.endInclusive) !== EmptyInterval
+
+    operator fun iterator(): Iterator> {
+        return object : Iterator> {
+            var next = root.lowestNode()
+
+            override fun hasNext(): Boolean {
+                return next !== terminator
+            }
+
+            override fun next(): Interval {
+                val node = next
+                next = next.next()
+                return node.interval
+            }
+        }
+    }
+
+    /**
+     * Adds the specified [Interval] to the interval tree.
+     */
+    operator fun plusAssign(interval: Interval) {
+        val node = Node(interval)
+
+        // Update the tree without doing any balancing
+        var current = root
+        var parent = terminator
+
+        while (current !== terminator) {
+            parent = current
+            current = if (node.interval.start <= current.interval.start) {
+                current.left
+            } else {
+                current.right
+            }
+        }
+
+        node.parent = parent
+
+        if (parent === terminator) {
+            root = node
+        } else {
+            if (node.interval.start <= parent.interval.start) {
+                parent.left = node
+            } else {
+                parent.right = node
+            }
+        }
+
+        updateNodeData(node)
+
+        rebalance(node)
+    }
+
+    private fun rebalance(target: Node) {
+        var node = target
+
+        while (node !== root && node.parent.color == TreeColor.Red) {
+            val ancestor = node.parent.parent
+            if (node.parent === ancestor.left) {
+                val right = ancestor.right
+                if (right.color == TreeColor.Red) {
+                    right.color = TreeColor.Black
+                    node.parent.color = TreeColor.Black
+                    ancestor.color = TreeColor.Red
+                    node = ancestor
+                } else {
+                    if (node === node.parent.right) {
+                        node = node.parent
+                        rotateLeft(node)
+                    }
+                    node.parent.color = TreeColor.Black
+                    ancestor.color = TreeColor.Red
+                    rotateRight(ancestor)
+                }
+            } else {
+                val left = ancestor.left
+                if (left.color == TreeColor.Red) {
+                    left.color = TreeColor.Black
+                    node.parent.color = TreeColor.Black
+                    ancestor.color = TreeColor.Red
+                    node = ancestor
+                } else {
+                    if (node === node.parent.left) {
+                        node = node.parent
+                        rotateRight(node)
+                    }
+                    node.parent.color = TreeColor.Black
+                    ancestor.color = TreeColor.Red
+                    rotateLeft(ancestor)
+                }
+            }
+        }
+
+        root.color = TreeColor.Black
+    }
+
+    private fun rotateLeft(node: Node) {
+        val right = node.right
+        node.right = right.left
+
+        if (right.left !== terminator) {
+            right.left.parent = node
+        }
+
+        right.parent = node.parent
+
+        if (node.parent === terminator) {
+            root = right
+        } else {
+            if (node.parent.left === node) {
+                node.parent.left = right
+            } else {
+                node.parent.right = right
+            }
+        }
+
+        right.left = node
+        node.parent = right
+
+        updateNodeData(node)
+    }
+
+    private fun rotateRight(node: Node) {
+        val left = node.left
+        node.left = left.right
+
+        if (left.right !== terminator) {
+            left.right.parent = node
+        }
+
+        left.parent = node.parent
+
+        if (node.parent === terminator) {
+            root = left
+        } else {
+            if (node.parent.right === node) {
+                node.parent.right = left
+            } else {
+                node.parent.left = left
+            }
+        }
+
+        left.right = node
+        node.parent = left
+
+        updateNodeData(node)
+    }
+
+    private fun updateNodeData(node: Node) {
+        var current = node
+        while (current !== terminator) {
+            current.min = min(current.interval.start, min(current.left.min, current.right.min))
+            current.max = max(current.interval.end, max(current.left.max, current.right.max))
+            current = current.parent
+        }
+    }
+
+    private enum class TreeColor {
+        Red, Black
+    }
+
+    private inner class Node(val interval: Interval, var color: TreeColor = TreeColor.Red) {
+        var min: Float = interval.start
+        var max: Float = interval.end
+
+        var left: Node = terminator
+        var right: Node = terminator
+        var parent: Node = terminator
+
+        fun lowestNode(): Node {
+            var node = this
+            while (node.left !== terminator) {
+                node = node.left
+            }
+            return node
+        }
+
+        fun next(): Node {
+            if (right !== terminator) {
+                return right.lowestNode()
+            }
+
+            var a = this
+            var b = parent
+            while (b !== terminator && a === b.right) {
+                a = b
+                b = b.parent
+            }
+
+            return b
+        }
+    }
+}
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PathEasing.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PathEasing.kt
index a704e39..45e1cd0 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PathEasing.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/PathEasing.kt
@@ -18,8 +18,7 @@
 
 import androidx.compose.runtime.Immutable
 import androidx.compose.ui.graphics.Path
-import androidx.compose.ui.graphics.PathMeasure
-import kotlin.math.absoluteValue
+import androidx.compose.ui.graphics.PathSegment
 
 /**
  * An easing function for an arbitrary [Path].
@@ -28,50 +27,30 @@
  * [Path] is the input value and the output is the y coordinate of the line at that
  * point. This means that the Path must conform to a function `y = f(x)`.
  *
- * The [Path] must not have gaps in the x direction and must not
- * loop back on itself such that there can be two points sharing the same x coordinate.
+ * The [Path] must be continuous along the x axis. The [Path] should also be
+ * monotonically increasing along the x axis. If the [Path] is not monotonic and
+ * there are multiple y values for a given x, the chosen y value is implementation
+ * dependent and may vary.
+ *
+ * The [Path] must not contain any [Path.close] command as it would force the path
+ * to restart from the beginning.
  *
  * This is equivalent to the Android `PathInterpolator`.
  *
- * [CubicBezierEasing] should be used if a bezier curve is required as it performs less allocations.
- * [PathEasing] should be used when creating an arbitrary path.
+ * [CubicBezierEasing] should be used if a single bezier curve is required as it
+ * performs fewer allocations. [PathEasing] should be used when creating an
+ * arbitrary path.
+ *
+ * Note: a [PathEasing] instance can be used from any thread, but not concurrently.
  *
  * @sample androidx.compose.animation.core.samples.PathEasingSample
  *
- * @param path The path to use to make the line representing the Easing Curve.
+ * @param path The [Path] to use to make the curve representing the easing curve.
  *
  */
 @Immutable
-class PathEasing(path: Path) : Easing {
-
-    private val offsetX: FloatArray
-    private val offsetY: FloatArray
-
-    init {
-        val pathMeasure = PathMeasure()
-        pathMeasure.setPath(path, false)
-
-        val pathLength: Float = pathMeasure.length
-        require(pathLength > 0) {
-            "Path cannot be zero in length. " +
-                "Ensure that supplied Path starts at [0,0] and ends at [1,1]"
-        }
-        val numPoints: Int =
-            (pathLength / Precision).toInt() + 1
-
-        offsetX = FloatArray(numPoints) { 0f }
-        offsetY = FloatArray(numPoints) { 0f }
-
-        for (i in 0 until numPoints) {
-            val distance = i * pathLength / (numPoints - 1)
-            val offset = pathMeasure.getPosition(distance)
-            offsetX[i] = offset.x
-            offsetY[i] = offset.y
-            if (i > 0 && offsetX[i] < offsetX[i - 1]) {
-                throw IllegalArgumentException("Path needs to be continuously increasing")
-            }
-        }
-    }
+class PathEasing(private val path: Path) : Easing {
+    private lateinit var intervals: IntervalTree
 
     override fun transform(fraction: Float): Float {
         if (fraction <= 0.0f) {
@@ -80,32 +59,48 @@
             return 1.0f
         }
 
-        // Do a binary search for the correct x to interpolate between.
-        val startIndex = offsetX.binarySearch(fraction)
-        // the index will be negative if an exact match is not found,
-        // so return the exact item if the index is positive.
-        if (startIndex > 0) {
-            return offsetY[startIndex]
+        if (!::intervals.isInitialized) {
+            val roots = FloatArray(5)
+
+            // Using an interval tree is a bit heavy handed but since we are dealing with
+            // easing curves, we don't expect many segments, and therefore few allocations.
+            // The interval tree allows us to quickly query for the correct segment inside
+            // the transform() function.
+            val segmentIntervals = IntervalTree().apply {
+                for (segment in path) {
+                    require(segment.type != PathSegment.Type.Close) {
+                        "The path cannot contain a close() command."
+                    }
+                    if (segment.type != PathSegment.Type.Move &&
+                        segment.type != PathSegment.Type.Done
+                    ) {
+                        val bounds = computeHorizontalBounds(segment, roots)
+                        this += Interval(bounds.first, bounds.second, segment)
+                    }
+                }
+            }
+
+            require(0.0f in segmentIntervals) {
+                "The easing path must start at 0.0f."
+            }
+
+            require(1.0f in segmentIntervals) {
+                "The easing path must end at 1.0f."
+            }
+
+            intervals = segmentIntervals
         }
-        val insertionStartIndex = startIndex.absoluteValue
-        if (insertionStartIndex >= offsetX.size - 1) {
-            return offsetY.last()
+
+        val result = intervals.findFirstOverlap(fraction)
+        val segment = checkNotNull(result.data) {
+            "The easing path is invalid. Make sure it is continuous on the x axis."
         }
-        val endIndex: Int = insertionStartIndex + 1
 
-        val xRange: Float = offsetX[endIndex] - offsetX[insertionStartIndex]
+        val t = findFirstRoot(segment, fraction)
+        check(!t.isNaN()) {
+            "The easing path is invalid. Make sure it does not contain NaN/Infinity values."
+        }
 
-        val tInRange: Float = fraction - offsetX[insertionStartIndex]
-        val newFraction = tInRange / xRange
-
-        val startY: Float = offsetY[insertionStartIndex]
-        val endY: Float = offsetY[endIndex]
-
-        return startY + newFraction * (endY - startY)
+        return evaluateY(segment, t).coerceAtLeast(0.0f).coerceAtMost(1.0f)
     }
 }
-
-/**
- * Governs the accuracy of the approximation of [PathEasing].
- */
-private const val Precision = 0.002f
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
index 1345611..b8dceda 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
@@ -417,7 +417,7 @@
 
     @Test
     fun testNonComposableFunctionReferenceWithStableExtensionReceiverMemoization() =
-        verifyComposeIrTransform(
+        verifyGoldenComposeIrTransform(
             extra = """
             class Stable
             fun Stable.foo() {}
@@ -433,39 +433,12 @@
                 val x = remember { Stable() }
                 val shouldMemoize = x::foo
             }
-        """,
-            expectedTransformed = """
-            @NonRestartableComposable
-            @Composable
-            fun Example(%composer: Composer?, %changed: Int) {
-              %composer.startReplaceableGroup(<>)
-              sourceInformation(%composer, "C(Example):Test.kt")
-              if (isTraceInProgress()) {
-                traceEventStart(<>, %changed, -1, <>)
-              }
-              val x = remember({
-                Stable()
-              }, %composer, 0)
-              val shouldMemoize = {
-                val tmp0 = x
-                %composer.startReplaceableGroup(<>)
-                val tmpCache = %composer.cache(%composer.changed(tmp0)) {
-                  tmp0::foo
-                }
-                %composer.endReplaceableGroup()
-                tmpCache
-              }
-              if (isTraceInProgress()) {
-                traceEventEnd()
-              }
-              %composer.endReplaceableGroup()
-            }
         """
         )
 
     @Test
     fun testNonComposableFunctionReferenceWithUnstableExtensionReceiverMemoization() =
-        verifyComposeIrTransform(
+        verifyGoldenComposeIrTransform(
             extra = """
             class Unstable {
                 var value: Int = 0
@@ -483,25 +456,6 @@
                 val x = remember { Unstable() }
                 val shouldNotMemoize = x::foo
             }
-        """,
-            expectedTransformed = """
-            @NonRestartableComposable
-            @Composable
-            fun Example(%composer: Composer?, %changed: Int) {
-              %composer.startReplaceableGroup(<>)
-              sourceInformation(%composer, "C(Example):Test.kt")
-              if (isTraceInProgress()) {
-                traceEventStart(<>, %changed, -1, <>)
-              }
-              val x = remember({
-                Unstable()
-              }, %composer, 0)
-              val shouldNotMemoize = x::foo
-              if (isTraceInProgress()) {
-                traceEventEnd()
-              }
-              %composer.endReplaceableGroup()
-            }
         """
         )
 
@@ -559,7 +513,7 @@
 
     @Test
     fun testNonComposableFunctionReferenceWithNoArgumentsMemoization() {
-        verifyComposeIrTransform(
+        verifyGoldenComposeIrTransform(
             source = """
                 import androidx.compose.runtime.Composable
                 import androidx.compose.runtime.remember
@@ -571,43 +525,6 @@
                     val x = remember { Stable() }
                     val shouldMemoize = x::qux
                 }
-            """,
-            expectedTransformed = """
-                @StabilityInferred(parameters = 1)
-                class Stable {
-                  fun qux() { }
-                  static val %stable: Int = 0
-                }
-                @Composable
-                fun Something(%composer: Composer?, %changed: Int) {
-                  %composer = %composer.startRestartGroup(<>)
-                  sourceInformation(%composer, "C(Something):Test.kt")
-                  if (%changed != 0 || !%composer.skipping) {
-                    if (isTraceInProgress()) {
-                      traceEventStart(<>, %changed, -1, <>)
-                    }
-                    val x = remember({
-                      Stable()
-                    }, %composer, 0)
-                    val shouldMemoize = {
-                      val tmp0 = x
-                      %composer.startReplaceableGroup(<>)
-                      val tmpCache = %composer.cache(%composer.changed(tmp0)) {
-                        tmp0::qux
-                      }
-                      %composer.endReplaceableGroup()
-                      tmpCache
-                    }
-                    if (isTraceInProgress()) {
-                      traceEventEnd()
-                    }
-                  } else {
-                    %composer.skipToGroupEnd()
-                  }
-                  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
-                    Something(%composer, updateChangedFlags(%changed or 0b0001))
-                  }
-                }
             """
         )
     }
@@ -615,7 +532,7 @@
     // Validate fix for b/302680514.
     @Test
     fun testNonComposableFunctionReferenceWithArgumentsMemoization() {
-        verifyComposeIrTransform(
+        verifyGoldenComposeIrTransform(
             source = """
                 import androidx.compose.runtime.Composable
                 import androidx.compose.runtime.remember
@@ -627,43 +544,6 @@
                     val x = remember { Stable() }
                     val shouldMemoize = x::qux
                 }
-            """,
-            expectedTransformed = """
-                @StabilityInferred(parameters = 1)
-                class Stable {
-                  fun qux(arg1: Any) { }
-                  static val %stable: Int = 0
-                }
-                @Composable
-                fun Something(%composer: Composer?, %changed: Int) {
-                  %composer = %composer.startRestartGroup(<>)
-                  sourceInformation(%composer, "C(Something):Test.kt")
-                  if (%changed != 0 || !%composer.skipping) {
-                    if (isTraceInProgress()) {
-                      traceEventStart(<>, %changed, -1, <>)
-                    }
-                    val x = remember({
-                      Stable()
-                    }, %composer, 0)
-                    val shouldMemoize = {
-                      val tmp0 = x
-                      %composer.startReplaceableGroup(<>)
-                      val tmpCache = %composer.cache(%composer.changed(tmp0)) {
-                        tmp0::qux
-                      }
-                      %composer.endReplaceableGroup()
-                      tmpCache
-                    }
-                    if (isTraceInProgress()) {
-                      traceEventEnd()
-                    }
-                  } else {
-                    %composer.skipToGroupEnd()
-                  }
-                  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
-                    Something(%composer, updateChangedFlags(%changed or 0b0001))
-                  }
-                }
             """
         )
     }
@@ -671,7 +551,7 @@
     // Reference to function with context receivers does not currently support memoization.
     @Test
     fun testNonComposableFunctionReferenceWithStableContextReceiverNotMemoized() {
-        verifyComposeIrTransform(
+        verifyGoldenComposeIrTransform(
             source = """
                 import androidx.compose.runtime.Composable
                 import androidx.compose.runtime.remember
@@ -687,39 +567,6 @@
                     val x = remember { Stable() }
                     val shouldNotMemoize = x::qux
                 }
-            """,
-            expectedTransformed = """
-                @StabilityInferred(parameters = 1)
-                class StableReceiver {
-                  static val %stable: Int = 0
-                }
-                @StabilityInferred(parameters = 1)
-                class Stable {
-                  fun qux(%context_receiver_0: StableReceiver) { }
-                  static val %stable: Int = 0
-                }
-                @Composable
-                fun Something(%composer: Composer?, %changed: Int) {
-                  %composer = %composer.startRestartGroup(<>)
-                  sourceInformation(%composer, "C(Something):Test.kt")
-                  if (%changed != 0 || !%composer.skipping) {
-                    if (isTraceInProgress()) {
-                      traceEventStart(<>, %changed, -1, <>)
-                    }
-                    val x = remember({
-                      Stable()
-                    }, %composer, 0)
-                    val shouldNotMemoize = x::qux
-                    if (isTraceInProgress()) {
-                      traceEventEnd()
-                    }
-                  } else {
-                    %composer.skipToGroupEnd()
-                  }
-                  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
-                    Something(%composer, updateChangedFlags(%changed or 0b0001))
-                  }
-                }
             """
         )
     }
@@ -787,4 +634,22 @@
             }
         """
     )
+
+    @Test
+    fun testMemoizingFunctionInIf() = verifyGoldenComposeIrTransform(
+        """
+            import androidx.compose.runtime.Composable
+
+            @Composable
+            fun Something(param: (() -> String)?) {
+                Something(
+                    if (param != null) {
+                        { param() }
+                    } else {
+                        null
+                    }
+                )
+            }
+        """
+    )
 }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
index ea6625d..87e0497 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
@@ -737,6 +737,18 @@
                 @Composable fun Wrapper(block: @Composable () -> Unit) {}
             """,
     )
+
+    @Test
+    fun testRememberExpressionMeta() = verifyGoldenComposeIrTransform(
+        source = """
+            import androidx.compose.runtime.*
+
+            @Composable fun Test(param: String) {
+                val a = remember { param }
+                Test(a)
+            }
+        """,
+    )
 }
 
 class RememberIntrinsicTransformTestsStrongSkipping(
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/StrongSkippingModeTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/StrongSkippingModeTransformTests.kt
index d1d6ba6..716343f 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/StrongSkippingModeTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/StrongSkippingModeTransformTests.kt
@@ -23,7 +23,6 @@
     FunctionBodySkippingTransformTestsBase(useFir) {
 
     override fun CompilerConfiguration.updateConfiguration() {
-        put(ComposeConfiguration.INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_KEY, false)
         put(ComposeConfiguration.STRONG_SKIPPING_ENABLED_KEY, true)
     }
 
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testApplyOnComposableCallResult\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testApplyOnComposableCallResult\133useFir = false\135.txt"
index d85e10f..ee7eaac 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testApplyOnComposableCallResult\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testApplyOnComposableCallResult\133useFir = false\135.txt"
@@ -28,11 +28,15 @@
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
-  val tmp0 = remember({
+  val tmp0 = %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+  val tmp1_group = %composer.cache(false) {
     mutableStateOf(
       value = value
     )
-  }, %composer, 0).apply {
+  }
+  %composer.endReplaceableGroup()
+  tmp1_group.apply {
     %this%apply.value = value
   }
   if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testApplyOnComposableCallResult\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testApplyOnComposableCallResult\133useFir = true\135.txt"
index d85e10f..ee7eaac 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testApplyOnComposableCallResult\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testApplyOnComposableCallResult\133useFir = true\135.txt"
@@ -28,11 +28,15 @@
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
-  val tmp0 = remember({
+  val tmp0 = %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+  val tmp1_group = %composer.cache(false) {
     mutableStateOf(
       value = value
     )
-  }, %composer, 0).apply {
+  }
+  %composer.endReplaceableGroup()
+  tmp1_group.apply {
     %this%apply.value = value
   }
   if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testEarlyReturnFromWhenStatement\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testEarlyReturnFromWhenStatement\133useFir = false\135.txt"
index efbf242..c008864 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testEarlyReturnFromWhenStatement\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testEarlyReturnFromWhenStatement\133useFir = false\135.txt"
@@ -25,17 +25,23 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %changed, -1, <>)
     }
-    val state = remember({
-      mutableStateOf(
-        value = false
-      )
-    }, %composer, 0)
+    val state = {
+      %composer.startReplaceableGroup(<>)
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(false) {
+        mutableStateOf(
+          value = false
+        )
+      }
+      %composer.endReplaceableGroup()
+      tmp0_group
+    }
     val tmp0_subject = state.value
     when {
       tmp0_subject == true -> {
         %composer.startReplaceableGroup(<>)
         sourceInformation(%composer, "")
-        val tmp0_return = Text("true", %composer, 0b0110)
+        val tmp1_return = Text("true", %composer, 0b0110)
         %composer.endReplaceableGroup()
         if (isTraceInProgress()) {
           traceEventEnd()
@@ -43,7 +49,7 @@
         %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
           Test(param, %composer, updateChangedFlags(%changed or 0b0001))
         }
-        return tmp0_return
+        return tmp1_return
       }
       else -> {
         %composer.startReplaceableGroup(<>)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testEarlyReturnFromWhenStatement\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testEarlyReturnFromWhenStatement\133useFir = true\135.txt"
index efbf242..c008864 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testEarlyReturnFromWhenStatement\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testEarlyReturnFromWhenStatement\133useFir = true\135.txt"
@@ -25,17 +25,23 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %changed, -1, <>)
     }
-    val state = remember({
-      mutableStateOf(
-        value = false
-      )
-    }, %composer, 0)
+    val state = {
+      %composer.startReplaceableGroup(<>)
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(false) {
+        mutableStateOf(
+          value = false
+        )
+      }
+      %composer.endReplaceableGroup()
+      tmp0_group
+    }
     val tmp0_subject = state.value
     when {
       tmp0_subject == true -> {
         %composer.startReplaceableGroup(<>)
         sourceInformation(%composer, "")
-        val tmp0_return = Text("true", %composer, 0b0110)
+        val tmp1_return = Text("true", %composer, 0b0110)
         %composer.endReplaceableGroup()
         if (isTraceInProgress()) {
           traceEventEnd()
@@ -43,7 +49,7 @@
         %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
           Test(param, %composer, updateChangedFlags(%changed or 0b0001))
         }
-        return tmp0_return
+        return tmp1_return
       }
       else -> {
         %composer.startReplaceableGroup(<>)
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupAroundExtensionFunctions\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupAroundExtensionFunctions\133useFir = false\135.txt"
index 2bfcc74..c63d5a6 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupAroundExtensionFunctions\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupAroundExtensionFunctions\133useFir = false\135.txt"
@@ -34,9 +34,15 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    val a = remember({
-      A()
-    }, %composer, 0)
+    val a = {
+      %composer.startReplaceableGroup(<>)
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(false) {
+        A()
+      }
+      %composer.endReplaceableGroup()
+      tmp0_group
+    }
     val  = start until end.iterator()
     while (.hasNext()) {
       val i = .next()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupAroundExtensionFunctions\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupAroundExtensionFunctions\133useFir = true\135.txt"
index 2bfcc74..c63d5a6 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupAroundExtensionFunctions\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testGroupAroundExtensionFunctions\133useFir = true\135.txt"
@@ -34,9 +34,15 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    val a = remember({
-      A()
-    }, %composer, 0)
+    val a = {
+      %composer.startReplaceableGroup(<>)
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(false) {
+        A()
+      }
+      %composer.endReplaceableGroup()
+      tmp0_group
+    }
     val  = start until end.iterator()
     while (.hasNext()) {
       val i = .next()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineArrayConstructor\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineArrayConstructor\133useFir = false\135.txt"
index 6912586..6806635 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineArrayConstructor\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineArrayConstructor\133useFir = false\135.txt"
@@ -34,58 +34,112 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     Array(n) { it: Int ->
-      val tmp0_return = remember({
-        it
-      }, %composer, 0)
-      tmp0_return
+      val tmp1_return = {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
+          it
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
+      }
+      tmp1_return
     }
     ByteArray(n) { it: Int ->
-      val tmp0_return = remember({
-        it.toByte()
-      }, %composer, 0)
-      tmp0_return
+      val tmp1_return = {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
+          it.toByte()
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
+      }
+      tmp1_return
     }
     CharArray(n) { it: Int ->
-      val tmp0_return = remember({
-        it.toChar()
-      }, %composer, 0)
-      tmp0_return
+      val tmp1_return = {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
+          it.toChar()
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
+      }
+      tmp1_return
     }
     ShortArray(n) { it: Int ->
-      val tmp0_return = remember({
-        it.toShort()
-      }, %composer, 0)
-      tmp0_return
+      val tmp1_return = {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
+          it.toShort()
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
+      }
+      tmp1_return
     }
     IntArray(n) { it: Int ->
-      val tmp0_return = remember({
-        it
-      }, %composer, 0)
-      tmp0_return
+      val tmp1_return = {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
+          it
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
+      }
+      tmp1_return
     }
     LongArray(n) { it: Int ->
-      val tmp0_return = remember({
-        it.toLong()
-      }, %composer, 0)
-      tmp0_return
+      val tmp1_return = {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
+          it.toLong()
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
+      }
+      tmp1_return
     }
     FloatArray(n) { it: Int ->
-      val tmp0_return = remember({
-        it.toFloat()
-      }, %composer, 0)
-      tmp0_return
+      val tmp1_return = {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
+          it.toFloat()
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
+      }
+      tmp1_return
     }
     DoubleArray(n) { it: Int ->
-      val tmp0_return = remember({
-        it.toDouble()
-      }, %composer, 0)
-      tmp0_return
+      val tmp1_return = {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
+          it.toDouble()
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
+      }
+      tmp1_return
     }
     BooleanArray(n) { it: Int ->
-      val tmp0_return = remember({
-        false
-      }, %composer, 0)
-      tmp0_return
+      val tmp1_return = {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
+          false
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
+      }
+      tmp1_return
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineArrayConstructor\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineArrayConstructor\133useFir = true\135.txt"
index 6912586..6806635 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineArrayConstructor\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testInlineArrayConstructor\133useFir = true\135.txt"
@@ -34,58 +34,112 @@
       traceEventStart(<>, %dirty, -1, <>)
     }
     Array(n) { it: Int ->
-      val tmp0_return = remember({
-        it
-      }, %composer, 0)
-      tmp0_return
+      val tmp1_return = {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
+          it
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
+      }
+      tmp1_return
     }
     ByteArray(n) { it: Int ->
-      val tmp0_return = remember({
-        it.toByte()
-      }, %composer, 0)
-      tmp0_return
+      val tmp1_return = {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
+          it.toByte()
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
+      }
+      tmp1_return
     }
     CharArray(n) { it: Int ->
-      val tmp0_return = remember({
-        it.toChar()
-      }, %composer, 0)
-      tmp0_return
+      val tmp1_return = {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
+          it.toChar()
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
+      }
+      tmp1_return
     }
     ShortArray(n) { it: Int ->
-      val tmp0_return = remember({
-        it.toShort()
-      }, %composer, 0)
-      tmp0_return
+      val tmp1_return = {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
+          it.toShort()
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
+      }
+      tmp1_return
     }
     IntArray(n) { it: Int ->
-      val tmp0_return = remember({
-        it
-      }, %composer, 0)
-      tmp0_return
+      val tmp1_return = {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
+          it
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
+      }
+      tmp1_return
     }
     LongArray(n) { it: Int ->
-      val tmp0_return = remember({
-        it.toLong()
-      }, %composer, 0)
-      tmp0_return
+      val tmp1_return = {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
+          it.toLong()
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
+      }
+      tmp1_return
     }
     FloatArray(n) { it: Int ->
-      val tmp0_return = remember({
-        it.toFloat()
-      }, %composer, 0)
-      tmp0_return
+      val tmp1_return = {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
+          it.toFloat()
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
+      }
+      tmp1_return
     }
     DoubleArray(n) { it: Int ->
-      val tmp0_return = remember({
-        it.toDouble()
-      }, %composer, 0)
-      tmp0_return
+      val tmp1_return = {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
+          it.toDouble()
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
+      }
+      tmp1_return
     }
     BooleanArray(n) { it: Int ->
-      val tmp0_return = remember({
-        false
-      }, %composer, 0)
-      tmp0_return
+      val tmp1_return = {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
+          false
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
+      }
+      tmp1_return
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInConditionalCallArgument\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInConditionalCallArgument\133useFir = false\135.txt"
index 6320122..dd26235 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInConditionalCallArgument\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInConditionalCallArgument\133useFir = false\135.txt"
@@ -34,15 +34,19 @@
     Test({
       %composer.startReplaceableGroup(<>)
       sourceInformation(%composer, "")
-      val tmp0_group = if (param == null) {
-        remember({
+      val tmp1_group = if (param == null) {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
           ""
-        }, %composer, 0)
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
       } else {
         null
       }
       %composer.endReplaceableGroup()
-      tmp0_group
+      tmp1_group
     }, %composer, 0)
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInConditionalCallArgument\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInConditionalCallArgument\133useFir = true\135.txt"
index 6320122..dd26235 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInConditionalCallArgument\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInConditionalCallArgument\133useFir = true\135.txt"
@@ -34,15 +34,19 @@
     Test({
       %composer.startReplaceableGroup(<>)
       sourceInformation(%composer, "")
-      val tmp0_group = if (param == null) {
-        remember({
+      val tmp1_group = if (param == null) {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
           ""
-        }, %composer, 0)
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
       } else {
         null
       }
       %composer.endReplaceableGroup()
-      tmp0_group
+      tmp1_group
     }, %composer, 0)
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInNestedConditionalCallArgument\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInNestedConditionalCallArgument\133useFir = false\135.txt"
index 18d298e..b3bbae4 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInNestedConditionalCallArgument\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInNestedConditionalCallArgument\133useFir = false\135.txt"
@@ -35,25 +35,29 @@
   val tmp0 = Test({
     %composer.startReplaceableGroup(<>)
     sourceInformation(%composer, "")
-    val tmp2_group = if (param == null) {
+    val tmp3_group = if (param == null) {
       Test({
         %composer.startReplaceableGroup(<>)
         sourceInformation(%composer, "")
-        val tmp1_group = if (param == null) {
-          remember({
+        val tmp2_group = if (param == null) {
+          %composer.startReplaceableGroup(<>)
+          sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+          val tmp1_group = %composer.cache(false) {
             ""
-          }, %composer, 0)
+          }
+          %composer.endReplaceableGroup()
+          tmp1_group
         } else {
           null
         }
         %composer.endReplaceableGroup()
-        tmp1_group
+        tmp2_group
       }, %composer, 0)
     } else {
       null
     }
     %composer.endReplaceableGroup()
-    tmp2_group
+    tmp3_group
   }, %composer, 0)
   if (isTraceInProgress()) {
     traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInNestedConditionalCallArgument\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInNestedConditionalCallArgument\133useFir = true\135.txt"
index 18d298e..b3bbae4 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInNestedConditionalCallArgument\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testRememberInNestedConditionalCallArgument\133useFir = true\135.txt"
@@ -35,25 +35,29 @@
   val tmp0 = Test({
     %composer.startReplaceableGroup(<>)
     sourceInformation(%composer, "")
-    val tmp2_group = if (param == null) {
+    val tmp3_group = if (param == null) {
       Test({
         %composer.startReplaceableGroup(<>)
         sourceInformation(%composer, "")
-        val tmp1_group = if (param == null) {
-          remember({
+        val tmp2_group = if (param == null) {
+          %composer.startReplaceableGroup(<>)
+          sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+          val tmp1_group = %composer.cache(false) {
             ""
-          }, %composer, 0)
+          }
+          %composer.endReplaceableGroup()
+          tmp1_group
         } else {
           null
         }
         %composer.endReplaceableGroup()
-        tmp1_group
+        tmp2_group
       }, %composer, 0)
     } else {
       null
     }
     %composer.endReplaceableGroup()
-    tmp2_group
+    tmp3_group
   }, %composer, 0)
   if (isTraceInProgress()) {
     traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testCaptureStableFunInterface\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testCaptureStableFunInterface\133useFir = false\135.txt"
index d939cac..00194e8 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testCaptureStableFunInterface\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testCaptureStableFunInterface\133useFir = false\135.txt"
@@ -27,7 +27,7 @@
 @Composable
 fun Test(int: Int, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Test):Test.kt")
+  sourceInformation(%composer, "C(Test)<{>,:Test.kt")
   val %dirty = %changed
   if (%changed and 0b1110 == 0) {
     %dirty = %dirty or if (%composer.changed(int)) 0b0100 else 0b0010
@@ -38,13 +38,14 @@
     }
     Example({
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changed(int)) {
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         Consumer { it: Int ->
           println(int)
         }
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testCaptureStableFunInterface\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testCaptureStableFunInterface\133useFir = true\135.txt"
index d939cac..00194e8 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testCaptureStableFunInterface\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionalInterfaceTransformTests/testCaptureStableFunInterface\133useFir = true\135.txt"
@@ -27,7 +27,7 @@
 @Composable
 fun Test(int: Int, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Test):Test.kt")
+  sourceInformation(%composer, "C(Test)<{>,:Test.kt")
   val %dirty = %changed
   if (%changed and 0b1110 == 0) {
     %dirty = %dirty or if (%composer.changed(int)) 0b0100 else 0b0010
@@ -38,13 +38,14 @@
     }
     Example({
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changed(int)) {
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         Consumer { it: Int ->
           println(int)
         }
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/memoizeLambdaInsideFunctionReturningValue\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/memoizeLambdaInsideFunctionReturningValue\133useFir = false\135.txt"
index 763ae86..e52b302 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/memoizeLambdaInsideFunctionReturningValue\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/memoizeLambdaInsideFunctionReturningValue\133useFir = false\135.txt"
@@ -15,19 +15,20 @@
 @Composable
 fun Test(foo: Foo, %composer: Composer?, %changed: Int): Int {
   %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test):Test.kt")
+  sourceInformation(%composer, "C(Test)<{>,:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val tmp0 = Consume({
     %composer.startReplaceableGroup(<>)
-    val tmpCache = %composer.cache(%composer.changed(foo)) {
+    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    val tmp1_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(foo) || %changed and 0b0110 == 0b0100) {
       {
         foo.value
       }
     }
     %composer.endReplaceableGroup()
-    tmpCache
+    tmp1_group
   }, %composer, 0)
   if (isTraceInProgress()) {
     traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/memoizeLambdaInsideFunctionReturningValue\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/memoizeLambdaInsideFunctionReturningValue\133useFir = true\135.txt"
index 763ae86..e52b302 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/memoizeLambdaInsideFunctionReturningValue\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/memoizeLambdaInsideFunctionReturningValue\133useFir = true\135.txt"
@@ -15,19 +15,20 @@
 @Composable
 fun Test(foo: Foo, %composer: Composer?, %changed: Int): Int {
   %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Test):Test.kt")
+  sourceInformation(%composer, "C(Test)<{>,:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
   val tmp0 = Consume({
     %composer.startReplaceableGroup(<>)
-    val tmpCache = %composer.cache(%composer.changed(foo)) {
+    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    val tmp1_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(foo) || %changed and 0b0110 == 0b0100) {
       {
         foo.value
       }
     }
     %composer.endReplaceableGroup()
-    tmpCache
+    tmp1_group
   }, %composer, 0)
   if (isTraceInProgress()) {
     traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testFunctionReferenceNonComposableMemoization\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testFunctionReferenceNonComposableMemoization\133useFir = false\135.txt"
index dbc703a..9f86c1e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testFunctionReferenceNonComposableMemoization\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testFunctionReferenceNonComposableMemoization\133useFir = false\135.txt"
@@ -15,7 +15,7 @@
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformation(%composer, "C(Example)<{>:Test.kt")
   val %dirty = %changed
   if (%changed and 0b1110 == 0) {
     %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010
@@ -29,13 +29,14 @@
     }
     val shouldMemoize = {
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changed(x)) {
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         {
           ::foo
         }
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testFunctionReferenceNonComposableMemoization\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testFunctionReferenceNonComposableMemoization\133useFir = true\135.txt"
index dbc703a..9f86c1e 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testFunctionReferenceNonComposableMemoization\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testFunctionReferenceNonComposableMemoization\133useFir = true\135.txt"
@@ -15,7 +15,7 @@
 @Composable
 fun Example(x: Int, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformation(%composer, "C(Example)<{>:Test.kt")
   val %dirty = %changed
   if (%changed and 0b1110 == 0) {
     %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010
@@ -29,13 +29,14 @@
     }
     val shouldMemoize = {
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changed(x)) {
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         {
           ::foo
         }
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLambdaDoesCapture\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLambdaDoesCapture\133useFir = false\135.txt"
index f680079..ecf2150 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLambdaDoesCapture\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLambdaDoesCapture\133useFir = false\135.txt"
@@ -46,7 +46,7 @@
 @Composable
 fun Test(a: String, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Test):Test.kt")
+  sourceInformation(%composer, "C(Test)<{>,:Test.kt")
   val %dirty = %changed
   if (%changed and 0b1110 == 0) {
     %dirty = %dirty or if (%composer.changed(a)) 0b0100 else 0b0010
@@ -57,13 +57,14 @@
     }
     TestLambda({
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changed(a)) {
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         {
           println("Captures a" + a)
         }
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLambdaDoesCapture\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLambdaDoesCapture\133useFir = true\135.txt"
index f680079..ecf2150 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLambdaDoesCapture\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLambdaDoesCapture\133useFir = true\135.txt"
@@ -46,7 +46,7 @@
 @Composable
 fun Test(a: String, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Test):Test.kt")
+  sourceInformation(%composer, "C(Test)<{>,:Test.kt")
   val %dirty = %changed
   if (%changed and 0b1110 == 0) {
     %dirty = %dirty or if (%composer.changed(a)) 0b0100 else 0b0010
@@ -57,13 +57,14 @@
     }
     TestLambda({
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changed(a)) {
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         {
           println("Captures a" + a)
         }
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures1\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures1\133useFir = false\135.txt"
index 0a8b775..87d306b 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures1\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures1\133useFir = false\135.txt"
@@ -25,7 +25,7 @@
 @Composable
 fun Err(y: Int, z: Int, %composer: Composer?, %changed: Int) {
   %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Err):Test.kt")
+  sourceInformation(%composer, "C(Err)<{>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -36,13 +36,14 @@
     }
   }
   %composer.startReplaceableGroup(<>)
-  val tmpCache = %composer.cache(%composer.changed(y) or %composer.changed(z)) {
+  sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+  val tmp0_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(y) || %changed and 0b0110 == 0b0100 or %changed and 0b01110000 xor 0b00110000 > 32 && %composer.changed(z) || %changed and 0b00110000 == 0b00100000) {
     {
       Local().something(2)
     }
   }
   %composer.endReplaceableGroup()
-  tmpCache
+  tmp0_group
   if (isTraceInProgress()) {
     traceEventEnd()
   }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures1\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures1\133useFir = true\135.txt"
index 0a8b775..87d306b 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures1\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures1\133useFir = true\135.txt"
@@ -25,7 +25,7 @@
 @Composable
 fun Err(y: Int, z: Int, %composer: Composer?, %changed: Int) {
   %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Err):Test.kt")
+  sourceInformation(%composer, "C(Err)<{>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -36,13 +36,14 @@
     }
   }
   %composer.startReplaceableGroup(<>)
-  val tmpCache = %composer.cache(%composer.changed(y) or %composer.changed(z)) {
+  sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+  val tmp0_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(y) || %changed and 0b0110 == 0b0100 or %changed and 0b01110000 xor 0b00110000 > 32 && %composer.changed(z) || %changed and 0b00110000 == 0b00100000) {
     {
       Local().something(2)
     }
   }
   %composer.endReplaceableGroup()
-  tmpCache
+  tmp0_group
   if (isTraceInProgress()) {
     traceEventEnd()
   }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures2\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures2\133useFir = false\135.txt"
index f51fe66..18fab7a 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures2\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures2\133useFir = false\135.txt"
@@ -22,7 +22,7 @@
 @Composable
 fun Example(z: Int, %composer: Composer?, %changed: Int) {
   %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformation(%composer, "C(Example)<{>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -31,13 +31,14 @@
   }
   val lambda = {
     %composer.startReplaceableGroup(<>)
-    val tmpCache = %composer.cache(%composer.changed(z)) {
+    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    val tmp0_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(z) || %changed and 0b0110 == 0b0100) {
       {
         Foo(1)
       }
     }
     %composer.endReplaceableGroup()
-    tmpCache
+    tmp0_group
   }
   if (isTraceInProgress()) {
     traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures2\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures2\133useFir = true\135.txt"
index f51fe66..18fab7a 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures2\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalClassCaptures2\133useFir = true\135.txt"
@@ -22,7 +22,7 @@
 @Composable
 fun Example(z: Int, %composer: Composer?, %changed: Int) {
   %composer.startReplaceableGroup(<>)
-  sourceInformation(%composer, "C(Example):Test.kt")
+  sourceInformation(%composer, "C(Example)<{>:Test.kt")
   if (isTraceInProgress()) {
     traceEventStart(<>, %changed, -1, <>)
   }
@@ -31,13 +31,14 @@
   }
   val lambda = {
     %composer.startReplaceableGroup(<>)
-    val tmpCache = %composer.cache(%composer.changed(z)) {
+    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    val tmp0_group = %composer.cache(%changed and 0b1110 xor 0b0110 > 4 && %composer.changed(z) || %changed and 0b0110 == 0b0100) {
       {
         Foo(1)
       }
     }
     %composer.endReplaceableGroup()
-    tmpCache
+    tmp0_group
   }
   if (isTraceInProgress()) {
     traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReferenceWReceiver\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReferenceWReceiver\133useFir = false\135.txt"
index 71e47429a1..8739b0c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReferenceWReceiver\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReferenceWReceiver\133useFir = false\135.txt"
@@ -19,7 +19,7 @@
 @Composable
 fun Something(param: String, rcvr: Int, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Something):Test.kt")
+  sourceInformation(%composer, "C(Something):Test.kt")
   val %dirty = %changed
   if (%changed and 0b1110 == 0) {
     %dirty = %dirty or if (%composer.changed(param)) 0b0100 else 0b0010
@@ -37,11 +37,12 @@
     val x = {
       val tmp0 = rcvr
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changed(param) or %composer.changed(tmp0)) {
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100 or %dirty and 0b01110000 == 0b00100000) {
         tmp0::method
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReferenceWReceiver\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReferenceWReceiver\133useFir = true\135.txt"
index 71e47429a1..6e1af1c 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReferenceWReceiver\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReferenceWReceiver\133useFir = true\135.txt"
@@ -19,7 +19,7 @@
 @Composable
 fun Something(param: String, rcvr: Int, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Something):Test.kt")
+  sourceInformation(%composer, "C(Something):Test.kt")
   val %dirty = %changed
   if (%changed and 0b1110 == 0) {
     %dirty = %dirty or if (%composer.changed(param)) 0b0100 else 0b0010
@@ -37,11 +37,12 @@
     val x = {
       val tmp0 = rcvr
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changed(param) or %composer.changed(tmp0)) {
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100 or %dirty and 0b01110000 == 0b00100000) {
         tmp0::method
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReference\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReference\133useFir = false\135.txt"
index c035785..444d05d 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReference\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReference\133useFir = false\135.txt"
@@ -19,7 +19,7 @@
 @Composable
 fun Something(param: String, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Something):Test.kt")
+  sourceInformation(%composer, "C(Something)<::meth...>:Test.kt")
   val %dirty = %changed
   if (%changed and 0b1110 == 0) {
     %dirty = %dirty or if (%composer.changed(param)) 0b0100 else 0b0010
@@ -33,11 +33,12 @@
     }
     val x = {
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changed(param)) {
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         ::method
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReference\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReference\133useFir = true\135.txt"
index c035785..9b24a99 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReference\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testLocalFunctionReference\133useFir = true\135.txt"
@@ -19,7 +19,7 @@
 @Composable
 fun Something(param: String, %composer: Composer?, %changed: Int) {
   %composer = %composer.startRestartGroup(<>)
-  sourceInformation(%composer, "C(Something):Test.kt")
+  sourceInformation(%composer, "C(Something):Test.kt")
   val %dirty = %changed
   if (%changed and 0b1110 == 0) {
     %dirty = %dirty or if (%composer.changed(param)) 0b0100 else 0b0010
@@ -33,11 +33,12 @@
     }
     val x = {
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changed(param)) {
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
         ::method
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testMemoizingFunctionInIf\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testMemoizingFunctionInIf\133useFir = false\135.txt"
new file mode 100644
index 0000000..6f62963
--- /dev/null
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testMemoizingFunctionInIf\133useFir = false\135.txt"
@@ -0,0 +1,62 @@
+//
+// Source
+// ------------------------------------------
+
+import androidx.compose.runtime.Composable
+
+@Composable
+fun Something(param: (() -> String)?) {
+    Something(
+        if (param != null) {
+            { param() }
+        } else {
+            null
+        }
+    )
+}
+
+//
+// Transformed IR
+// ------------------------------------------
+
+@Composable
+fun Something(param: Function0?, %composer: Composer?, %changed: Int) {
+  %composer = %composer.startRestartGroup(<>)
+  sourceInformation(%composer, "C(Something):Test.kt")
+  val %dirty = %changed
+  if (%changed and 0b1110 == 0) {
+    %dirty = %dirty or if (%composer.changedInstance(param)) 0b0100 else 0b0010
+  }
+  if (%dirty and 0b1011 != 0b0010 || !%composer.skipping) {
+    if (isTraceInProgress()) {
+      traceEventStart(<>, %dirty, -1, <>)
+    }
+    Something({
+      %composer.startReplaceableGroup(<>)
+      sourceInformation(%composer, "<{>")
+      val tmp1_group = if (param != null) {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
+          {
+            param()
+          }
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
+      } else {
+        null
+      }
+      %composer.endReplaceableGroup()
+      tmp1_group
+    }, %composer, 0)
+    if (isTraceInProgress()) {
+      traceEventEnd()
+    }
+  } else {
+    %composer.skipToGroupEnd()
+  }
+  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+    Something(param, %composer, updateChangedFlags(%changed or 0b0001))
+  }
+}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testMemoizingFunctionInIf\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testMemoizingFunctionInIf\133useFir = true\135.txt"
new file mode 100644
index 0000000..6f62963
--- /dev/null
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testMemoizingFunctionInIf\133useFir = true\135.txt"
@@ -0,0 +1,62 @@
+//
+// Source
+// ------------------------------------------
+
+import androidx.compose.runtime.Composable
+
+@Composable
+fun Something(param: (() -> String)?) {
+    Something(
+        if (param != null) {
+            { param() }
+        } else {
+            null
+        }
+    )
+}
+
+//
+// Transformed IR
+// ------------------------------------------
+
+@Composable
+fun Something(param: Function0?, %composer: Composer?, %changed: Int) {
+  %composer = %composer.startRestartGroup(<>)
+  sourceInformation(%composer, "C(Something):Test.kt")
+  val %dirty = %changed
+  if (%changed and 0b1110 == 0) {
+    %dirty = %dirty or if (%composer.changedInstance(param)) 0b0100 else 0b0010
+  }
+  if (%dirty and 0b1011 != 0b0010 || !%composer.skipping) {
+    if (isTraceInProgress()) {
+      traceEventStart(<>, %dirty, -1, <>)
+    }
+    Something({
+      %composer.startReplaceableGroup(<>)
+      sourceInformation(%composer, "<{>")
+      val tmp1_group = if (param != null) {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(%dirty and 0b1110 == 0b0100) {
+          {
+            param()
+          }
+        }
+        %composer.endReplaceableGroup()
+        tmp0_group
+      } else {
+        null
+      }
+      %composer.endReplaceableGroup()
+      tmp1_group
+    }, %composer, 0)
+    if (isTraceInProgress()) {
+      traceEventEnd()
+    }
+  } else {
+    %composer.skipToGroupEnd()
+  }
+  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+    Something(param, %composer, updateChangedFlags(%changed or 0b0001))
+  }
+}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithArgumentsMemoization\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithArgumentsMemoization\133useFir = false\135.txt"
new file mode 100644
index 0000000..6ac271b
--- /dev/null
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithArgumentsMemoization\133useFir = false\135.txt"
@@ -0,0 +1,61 @@
+//
+// Source
+// ------------------------------------------
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+
+class Stable { fun qux(arg1: Any) {} }
+
+@Composable
+fun Something() {
+    val x = remember { Stable() }
+    val shouldMemoize = x::qux
+}
+
+//
+// Transformed IR
+// ------------------------------------------
+
+@StabilityInferred(parameters = 1)
+class Stable {
+  fun qux(arg1: Any) { }
+  static val %stable: Int = 0
+}
+@Composable
+fun Something(%composer: Composer?, %changed: Int) {
+  %composer = %composer.startRestartGroup(<>)
+  sourceInformation(%composer, "C(Something),:Test.kt")
+  if (%changed != 0 || !%composer.skipping) {
+    if (isTraceInProgress()) {
+      traceEventStart(<>, %changed, -1, <>)
+    }
+    val x = {
+      %composer.startReplaceableGroup(<>)
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(false) {
+        Stable()
+      }
+      %composer.endReplaceableGroup()
+      tmp0_group
+    }
+    val shouldMemoize = {
+      val tmp0 = x
+      %composer.startReplaceableGroup(<>)
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp1_group = %composer.cache(false) {
+        tmp0::qux
+      }
+      %composer.endReplaceableGroup()
+      tmp1_group
+    }
+    if (isTraceInProgress()) {
+      traceEventEnd()
+    }
+  } else {
+    %composer.skipToGroupEnd()
+  }
+  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+    Something(%composer, updateChangedFlags(%changed or 0b0001))
+  }
+}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithArgumentsMemoization\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithArgumentsMemoization\133useFir = true\135.txt"
new file mode 100644
index 0000000..a1bbcd7
--- /dev/null
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithArgumentsMemoization\133useFir = true\135.txt"
@@ -0,0 +1,61 @@
+//
+// Source
+// ------------------------------------------
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+
+class Stable { fun qux(arg1: Any) {} }
+
+@Composable
+fun Something() {
+    val x = remember { Stable() }
+    val shouldMemoize = x::qux
+}
+
+//
+// Transformed IR
+// ------------------------------------------
+
+@StabilityInferred(parameters = 1)
+class Stable {
+  fun qux(arg1: Any) { }
+  static val %stable: Int = 0
+}
+@Composable
+fun Something(%composer: Composer?, %changed: Int) {
+  %composer = %composer.startRestartGroup(<>)
+  sourceInformation(%composer, "C(Something),:Test.kt")
+  if (%changed != 0 || !%composer.skipping) {
+    if (isTraceInProgress()) {
+      traceEventStart(<>, %changed, -1, <>)
+    }
+    val x = {
+      %composer.startReplaceableGroup(<>)
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(false) {
+        Stable()
+      }
+      %composer.endReplaceableGroup()
+      tmp0_group
+    }
+    val shouldMemoize = {
+      val tmp0 = x
+      %composer.startReplaceableGroup(<>)
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp1_group = %composer.cache(false) {
+        tmp0::qux
+      }
+      %composer.endReplaceableGroup()
+      tmp1_group
+    }
+    if (isTraceInProgress()) {
+      traceEventEnd()
+    }
+  } else {
+    %composer.skipToGroupEnd()
+  }
+  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+    Something(%composer, updateChangedFlags(%changed or 0b0001))
+  }
+}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithNoArgumentsMemoization\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithNoArgumentsMemoization\133useFir = false\135.txt"
new file mode 100644
index 0000000..1f91ed8
--- /dev/null
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithNoArgumentsMemoization\133useFir = false\135.txt"
@@ -0,0 +1,61 @@
+//
+// Source
+// ------------------------------------------
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+
+class Stable { fun qux() {} }
+
+@Composable
+fun Something() {
+    val x = remember { Stable() }
+    val shouldMemoize = x::qux
+}
+
+//
+// Transformed IR
+// ------------------------------------------
+
+@StabilityInferred(parameters = 1)
+class Stable {
+  fun qux() { }
+  static val %stable: Int = 0
+}
+@Composable
+fun Something(%composer: Composer?, %changed: Int) {
+  %composer = %composer.startRestartGroup(<>)
+  sourceInformation(%composer, "C(Something),:Test.kt")
+  if (%changed != 0 || !%composer.skipping) {
+    if (isTraceInProgress()) {
+      traceEventStart(<>, %changed, -1, <>)
+    }
+    val x = {
+      %composer.startReplaceableGroup(<>)
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(false) {
+        Stable()
+      }
+      %composer.endReplaceableGroup()
+      tmp0_group
+    }
+    val shouldMemoize = {
+      val tmp0 = x
+      %composer.startReplaceableGroup(<>)
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp1_group = %composer.cache(false) {
+        tmp0::qux
+      }
+      %composer.endReplaceableGroup()
+      tmp1_group
+    }
+    if (isTraceInProgress()) {
+      traceEventEnd()
+    }
+  } else {
+    %composer.skipToGroupEnd()
+  }
+  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+    Something(%composer, updateChangedFlags(%changed or 0b0001))
+  }
+}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithNoArgumentsMemoization\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithNoArgumentsMemoization\133useFir = true\135.txt"
new file mode 100644
index 0000000..a169c19
--- /dev/null
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithNoArgumentsMemoization\133useFir = true\135.txt"
@@ -0,0 +1,61 @@
+//
+// Source
+// ------------------------------------------
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+
+class Stable { fun qux() {} }
+
+@Composable
+fun Something() {
+    val x = remember { Stable() }
+    val shouldMemoize = x::qux
+}
+
+//
+// Transformed IR
+// ------------------------------------------
+
+@StabilityInferred(parameters = 1)
+class Stable {
+  fun qux() { }
+  static val %stable: Int = 0
+}
+@Composable
+fun Something(%composer: Composer?, %changed: Int) {
+  %composer = %composer.startRestartGroup(<>)
+  sourceInformation(%composer, "C(Something),:Test.kt")
+  if (%changed != 0 || !%composer.skipping) {
+    if (isTraceInProgress()) {
+      traceEventStart(<>, %changed, -1, <>)
+    }
+    val x = {
+      %composer.startReplaceableGroup(<>)
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(false) {
+        Stable()
+      }
+      %composer.endReplaceableGroup()
+      tmp0_group
+    }
+    val shouldMemoize = {
+      val tmp0 = x
+      %composer.startReplaceableGroup(<>)
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp1_group = %composer.cache(false) {
+        tmp0::qux
+      }
+      %composer.endReplaceableGroup()
+      tmp1_group
+    }
+    if (isTraceInProgress()) {
+      traceEventEnd()
+    }
+  } else {
+    %composer.skipToGroupEnd()
+  }
+  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+    Something(%composer, updateChangedFlags(%changed or 0b0001))
+  }
+}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableContextReceiverNotMemoized\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableContextReceiverNotMemoized\133useFir = false\135.txt"
new file mode 100644
index 0000000..7511d6d
--- /dev/null
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableContextReceiverNotMemoized\133useFir = false\135.txt"
@@ -0,0 +1,60 @@
+//
+// Source
+// ------------------------------------------
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+
+class StableReceiver
+class Stable {
+    context(StableReceiver)
+    fun qux() {}
+}
+
+@Composable
+fun Something() {
+    val x = remember { Stable() }
+    val shouldNotMemoize = x::qux
+}
+
+//
+// Transformed IR
+// ------------------------------------------
+
+@StabilityInferred(parameters = 1)
+class StableReceiver {
+  static val %stable: Int = 0
+}
+@StabilityInferred(parameters = 1)
+class Stable {
+  fun qux(%context_receiver_0: StableReceiver) { }
+  static val %stable: Int = 0
+}
+@Composable
+fun Something(%composer: Composer?, %changed: Int) {
+  %composer = %composer.startRestartGroup(<>)
+  sourceInformation(%composer, "C(Something):Test.kt")
+  if (%changed != 0 || !%composer.skipping) {
+    if (isTraceInProgress()) {
+      traceEventStart(<>, %changed, -1, <>)
+    }
+    val x = {
+      %composer.startReplaceableGroup(<>)
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(false) {
+        Stable()
+      }
+      %composer.endReplaceableGroup()
+      tmp0_group
+    }
+    val shouldNotMemoize = x::qux
+    if (isTraceInProgress()) {
+      traceEventEnd()
+    }
+  } else {
+    %composer.skipToGroupEnd()
+  }
+  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+    Something(%composer, updateChangedFlags(%changed or 0b0001))
+  }
+}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableContextReceiverNotMemoized\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableContextReceiverNotMemoized\133useFir = true\135.txt"
new file mode 100644
index 0000000..7511d6d
--- /dev/null
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableContextReceiverNotMemoized\133useFir = true\135.txt"
@@ -0,0 +1,60 @@
+//
+// Source
+// ------------------------------------------
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+
+class StableReceiver
+class Stable {
+    context(StableReceiver)
+    fun qux() {}
+}
+
+@Composable
+fun Something() {
+    val x = remember { Stable() }
+    val shouldNotMemoize = x::qux
+}
+
+//
+// Transformed IR
+// ------------------------------------------
+
+@StabilityInferred(parameters = 1)
+class StableReceiver {
+  static val %stable: Int = 0
+}
+@StabilityInferred(parameters = 1)
+class Stable {
+  fun qux(%context_receiver_0: StableReceiver) { }
+  static val %stable: Int = 0
+}
+@Composable
+fun Something(%composer: Composer?, %changed: Int) {
+  %composer = %composer.startRestartGroup(<>)
+  sourceInformation(%composer, "C(Something):Test.kt")
+  if (%changed != 0 || !%composer.skipping) {
+    if (isTraceInProgress()) {
+      traceEventStart(<>, %changed, -1, <>)
+    }
+    val x = {
+      %composer.startReplaceableGroup(<>)
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(false) {
+        Stable()
+      }
+      %composer.endReplaceableGroup()
+      tmp0_group
+    }
+    val shouldNotMemoize = x::qux
+    if (isTraceInProgress()) {
+      traceEventEnd()
+    }
+  } else {
+    %composer.skipToGroupEnd()
+  }
+  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+    Something(%composer, updateChangedFlags(%changed or 0b0001))
+  }
+}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableExtensionReceiverMemoization\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableExtensionReceiverMemoization\133useFir = false\135.txt"
new file mode 100644
index 0000000..3b246ec
--- /dev/null
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableExtensionReceiverMemoization\133useFir = false\135.txt"
@@ -0,0 +1,51 @@
+//
+// Source
+// ------------------------------------------
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.NonRestartableComposable
+import androidx.compose.runtime.remember
+
+@NonRestartableComposable
+@Composable
+fun Example() {
+    val x = remember { Stable() }
+    val shouldMemoize = x::foo
+}
+
+//
+// Transformed IR
+// ------------------------------------------
+
+@NonRestartableComposable
+@Composable
+fun Example(%composer: Composer?, %changed: Int) {
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "C(Example),:Test.kt")
+  if (isTraceInProgress()) {
+    traceEventStart(<>, %changed, -1, <>)
+  }
+  val x = {
+    %composer.startReplaceableGroup(<>)
+    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    val tmp0_group = %composer.cache(false) {
+      Stable()
+    }
+    %composer.endReplaceableGroup()
+    tmp0_group
+  }
+  val shouldMemoize = {
+    val tmp0 = x
+    %composer.startReplaceableGroup(<>)
+    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    val tmp1_group = %composer.cache(false) {
+      tmp0::foo
+    }
+    %composer.endReplaceableGroup()
+    tmp1_group
+  }
+  if (isTraceInProgress()) {
+    traceEventEnd()
+  }
+  %composer.endReplaceableGroup()
+}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableExtensionReceiverMemoization\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableExtensionReceiverMemoization\133useFir = true\135.txt"
new file mode 100644
index 0000000..d1ea51b
--- /dev/null
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithStableExtensionReceiverMemoization\133useFir = true\135.txt"
@@ -0,0 +1,51 @@
+//
+// Source
+// ------------------------------------------
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.NonRestartableComposable
+import androidx.compose.runtime.remember
+
+@NonRestartableComposable
+@Composable
+fun Example() {
+    val x = remember { Stable() }
+    val shouldMemoize = x::foo
+}
+
+//
+// Transformed IR
+// ------------------------------------------
+
+@NonRestartableComposable
+@Composable
+fun Example(%composer: Composer?, %changed: Int) {
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "C(Example),:Test.kt")
+  if (isTraceInProgress()) {
+    traceEventStart(<>, %changed, -1, <>)
+  }
+  val x = {
+    %composer.startReplaceableGroup(<>)
+    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    val tmp0_group = %composer.cache(false) {
+      Stable()
+    }
+    %composer.endReplaceableGroup()
+    tmp0_group
+  }
+  val shouldMemoize = {
+    val tmp0 = x
+    %composer.startReplaceableGroup(<>)
+    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    val tmp1_group = %composer.cache(false) {
+      tmp0::foo
+    }
+    %composer.endReplaceableGroup()
+    tmp1_group
+  }
+  if (isTraceInProgress()) {
+    traceEventEnd()
+  }
+  %composer.endReplaceableGroup()
+}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithUnstableExtensionReceiverMemoization\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithUnstableExtensionReceiverMemoization\133useFir = false\135.txt"
new file mode 100644
index 0000000..232587d
--- /dev/null
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithUnstableExtensionReceiverMemoization\133useFir = false\135.txt"
@@ -0,0 +1,42 @@
+//
+// Source
+// ------------------------------------------
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.NonRestartableComposable
+import androidx.compose.runtime.remember
+
+@NonRestartableComposable
+@Composable
+fun Example() {
+    val x = remember { Unstable() }
+    val shouldNotMemoize = x::foo
+}
+
+//
+// Transformed IR
+// ------------------------------------------
+
+@NonRestartableComposable
+@Composable
+fun Example(%composer: Composer?, %changed: Int) {
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "C(Example):Test.kt")
+  if (isTraceInProgress()) {
+    traceEventStart(<>, %changed, -1, <>)
+  }
+  val x = {
+    %composer.startReplaceableGroup(<>)
+    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    val tmp0_group = %composer.cache(false) {
+      Unstable()
+    }
+    %composer.endReplaceableGroup()
+    tmp0_group
+  }
+  val shouldNotMemoize = x::foo
+  if (isTraceInProgress()) {
+    traceEventEnd()
+  }
+  %composer.endReplaceableGroup()
+}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithUnstableExtensionReceiverMemoization\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithUnstableExtensionReceiverMemoization\133useFir = true\135.txt"
new file mode 100644
index 0000000..232587d
--- /dev/null
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testNonComposableFunctionReferenceWithUnstableExtensionReceiverMemoization\133useFir = true\135.txt"
@@ -0,0 +1,42 @@
+//
+// Source
+// ------------------------------------------
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.NonRestartableComposable
+import androidx.compose.runtime.remember
+
+@NonRestartableComposable
+@Composable
+fun Example() {
+    val x = remember { Unstable() }
+    val shouldNotMemoize = x::foo
+}
+
+//
+// Transformed IR
+// ------------------------------------------
+
+@NonRestartableComposable
+@Composable
+fun Example(%composer: Composer?, %changed: Int) {
+  %composer.startReplaceableGroup(<>)
+  sourceInformation(%composer, "C(Example):Test.kt")
+  if (isTraceInProgress()) {
+    traceEventStart(<>, %changed, -1, <>)
+  }
+  val x = {
+    %composer.startReplaceableGroup(<>)
+    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    val tmp0_group = %composer.cache(false) {
+      Unstable()
+    }
+    %composer.endReplaceableGroup()
+    tmp0_group
+  }
+  val shouldNotMemoize = x::foo
+  if (isTraceInProgress()) {
+    traceEventEnd()
+  }
+  %composer.endReplaceableGroup()
+}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testRememberComposableLambda\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testRememberComposableLambda\133useFir = false\135.txt"
index 0b28cfa..f8d75e2 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testRememberComposableLambda\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testRememberComposableLambda\133useFir = false\135.txt"
@@ -26,7 +26,9 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    remember({
+    %composer.startReplaceableGroup(<>)
+    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    val tmp0_group = %composer.cache(false) {
       composableLambdaInstance(<>, true) { %composer: Composer?, %changed: Int ->
         sourceInformation(%composer, "C:Test.kt")
         if (%changed and 0b1011 != 0b0010 || !%composer.skipping) {
@@ -41,7 +43,9 @@
           %composer.skipToGroupEnd()
         }
       }
-    }, %composer, 0)(%composer, 6)
+    }
+    %composer.endReplaceableGroup()
+    tmp0_group(%composer, 6)
     %composer.cache(false) {
       composableLambdaInstance(<>, true) { %composer: Composer?, %changed: Int ->
         sourceInformation(%composer, "C:Test.kt")
@@ -58,7 +62,7 @@
         }
       }
     }
-    (%composer, 6)
+    (%composer, 0)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testRememberComposableLambda\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testRememberComposableLambda\133useFir = true\135.txt"
index 0b28cfa..f8d75e2 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testRememberComposableLambda\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.LambdaMemoizationTransformTests/testRememberComposableLambda\133useFir = true\135.txt"
@@ -26,7 +26,9 @@
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
-    remember({
+    %composer.startReplaceableGroup(<>)
+    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    val tmp0_group = %composer.cache(false) {
       composableLambdaInstance(<>, true) { %composer: Composer?, %changed: Int ->
         sourceInformation(%composer, "C:Test.kt")
         if (%changed and 0b1011 != 0b0010 || !%composer.skipping) {
@@ -41,7 +43,9 @@
           %composer.skipToGroupEnd()
         }
       }
-    }, %composer, 0)(%composer, 6)
+    }
+    %composer.endReplaceableGroup()
+    tmp0_group(%composer, 6)
     %composer.cache(false) {
       composableLambdaInstance(<>, true) { %composer: Composer?, %changed: Int ->
         sourceInformation(%composer, "C:Test.kt")
@@ -58,7 +62,7 @@
         }
       }
     }
-    (%composer, 6)
+    (%composer, 0)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_AfterComposable\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_AfterComposable\133useFir = false\135.txt"
index 780a5a0..98b7759 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_AfterComposable\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_AfterComposable\133useFir = false\135.txt"
@@ -22,14 +22,18 @@
   %composer = %composer.startRestartGroup(<>)
   sourceInformation(%composer, "C(Test),,:Test.kt")
   val %dirty = %changed
-  if (%changed and 0b1110 == 0) {
-    %dirty = %dirty or if (%default and 0b0001 == 0 && %composer.changed(a)) 0b0100 else 0b0010
+  if (%default and 0b0001 != 0) {
+    %dirty = %dirty or 0b0110
+  } else if (%changed and 0b1110 == 0) {
+    %dirty = %dirty or if (%composer.changed(a)) 0b0100 else 0b0010
   }
   if (%changed and 0b01110000 == 0) {
     %dirty = %dirty or if (%default and 0b0010 == 0 && %composer.changed(b)) 0b00100000 else 0b00010000
   }
-  if (%changed and 0b001110000000 == 0) {
-    %dirty = %dirty or if (%default and 0b0100 == 0 && %composer.changed(c)) 0b000100000000 else 0b10000000
+  if (%default and 0b0100 != 0) {
+    %dirty = %dirty or 0b000110000000
+  } else if (%changed and 0b001110000000 == 0) {
+    %dirty = %dirty or if (%composer.changed(c)) 0b000100000000 else 0b10000000
   }
   if (%dirty and 0b001011011011 != 0b10010010 || !%composer.skipping) {
     %composer.startDefaults()
@@ -44,7 +48,6 @@
           %composer.endReplaceableGroup()
           tmp0_group
         }
-        %dirty = %dirty and 0b1110.inv()
       }
       if (%default and 0b0010 != 0) {
         b = SomeComposable(%composer, 0)
@@ -60,19 +63,12 @@
           %composer.endReplaceableGroup()
           tmp1_group
         }
-        %dirty = %dirty and 0b001110000000.inv()
       }
     } else {
       %composer.skipToGroupEnd()
-      if (%default and 0b0001 != 0) {
-        %dirty = %dirty and 0b1110.inv()
-      }
       if (%default and 0b0010 != 0) {
         %dirty = %dirty and 0b01110000.inv()
       }
-      if (%default and 0b0100 != 0) {
-        %dirty = %dirty and 0b001110000000.inv()
-      }
     }
     %composer.endDefaults()
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_AfterComposable\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_AfterComposable\133useFir = true\135.txt"
index 780a5a0..98b7759 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_AfterComposable\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_AfterComposable\133useFir = true\135.txt"
@@ -22,14 +22,18 @@
   %composer = %composer.startRestartGroup(<>)
   sourceInformation(%composer, "C(Test),,:Test.kt")
   val %dirty = %changed
-  if (%changed and 0b1110 == 0) {
-    %dirty = %dirty or if (%default and 0b0001 == 0 && %composer.changed(a)) 0b0100 else 0b0010
+  if (%default and 0b0001 != 0) {
+    %dirty = %dirty or 0b0110
+  } else if (%changed and 0b1110 == 0) {
+    %dirty = %dirty or if (%composer.changed(a)) 0b0100 else 0b0010
   }
   if (%changed and 0b01110000 == 0) {
     %dirty = %dirty or if (%default and 0b0010 == 0 && %composer.changed(b)) 0b00100000 else 0b00010000
   }
-  if (%changed and 0b001110000000 == 0) {
-    %dirty = %dirty or if (%default and 0b0100 == 0 && %composer.changed(c)) 0b000100000000 else 0b10000000
+  if (%default and 0b0100 != 0) {
+    %dirty = %dirty or 0b000110000000
+  } else if (%changed and 0b001110000000 == 0) {
+    %dirty = %dirty or if (%composer.changed(c)) 0b000100000000 else 0b10000000
   }
   if (%dirty and 0b001011011011 != 0b10010010 || !%composer.skipping) {
     %composer.startDefaults()
@@ -44,7 +48,6 @@
           %composer.endReplaceableGroup()
           tmp0_group
         }
-        %dirty = %dirty and 0b1110.inv()
       }
       if (%default and 0b0010 != 0) {
         b = SomeComposable(%composer, 0)
@@ -60,19 +63,12 @@
           %composer.endReplaceableGroup()
           tmp1_group
         }
-        %dirty = %dirty and 0b001110000000.inv()
       }
     } else {
       %composer.skipToGroupEnd()
-      if (%default and 0b0001 != 0) {
-        %dirty = %dirty and 0b1110.inv()
-      }
       if (%default and 0b0010 != 0) {
         %dirty = %dirty and 0b01110000.inv()
       }
-      if (%default and 0b0100 != 0) {
-        %dirty = %dirty and 0b001110000000.inv()
-      }
     }
     %composer.endDefaults()
     if (isTraceInProgress()) {
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_Simple\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_Simple\133useFir = false\135.txt"
index 15f468e..b7e60f1 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_Simple\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_Simple\133useFir = false\135.txt"
@@ -20,31 +20,23 @@
   %composer = %composer.startRestartGroup(<>)
   sourceInformation(%composer, "C(Test):Test.kt")
   val %dirty = %changed
-  if (%changed and 0b1110 == 0) {
-    %dirty = %dirty or if (%default and 0b0001 == 0 && %composer.changed(a)) 0b0100 else 0b0010
+  if (%default and 0b0001 != 0) {
+    %dirty = %dirty or 0b0110
+  } else if (%changed and 0b1110 == 0) {
+    %dirty = %dirty or if (%composer.changed(a)) 0b0100 else 0b0010
   }
   if (%dirty and 0b1011 != 0b0010 || !%composer.skipping) {
-    %composer.startDefaults()
-    if (%changed and 0b0001 == 0 || %composer.defaultsInvalid) {
-      if (%default and 0b0001 != 0) {
-        a = {
-          %composer.startReplaceableGroup(<>)
-          sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
-          val tmp0_group = %composer.cache(false) {
-            0
-          }
-          %composer.endReplaceableGroup()
-          tmp0_group
+    if (%default and 0b0001 != 0) {
+      a = {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
+          0
         }
-        %dirty = %dirty and 0b1110.inv()
-      }
-    } else {
-      %composer.skipToGroupEnd()
-      if (%default and 0b0001 != 0) {
-        %dirty = %dirty and 0b1110.inv()
+        %composer.endReplaceableGroup()
+        tmp0_group
       }
     }
-    %composer.endDefaults()
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_Simple\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_Simple\133useFir = true\135.txt"
index 15f468e..b7e60f1 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_Simple\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testIntrinsicRememberOfDefaultParameters_Simple\133useFir = true\135.txt"
@@ -20,31 +20,23 @@
   %composer = %composer.startRestartGroup(<>)
   sourceInformation(%composer, "C(Test):Test.kt")
   val %dirty = %changed
-  if (%changed and 0b1110 == 0) {
-    %dirty = %dirty or if (%default and 0b0001 == 0 && %composer.changed(a)) 0b0100 else 0b0010
+  if (%default and 0b0001 != 0) {
+    %dirty = %dirty or 0b0110
+  } else if (%changed and 0b1110 == 0) {
+    %dirty = %dirty or if (%composer.changed(a)) 0b0100 else 0b0010
   }
   if (%dirty and 0b1011 != 0b0010 || !%composer.skipping) {
-    %composer.startDefaults()
-    if (%changed and 0b0001 == 0 || %composer.defaultsInvalid) {
-      if (%default and 0b0001 != 0) {
-        a = {
-          %composer.startReplaceableGroup(<>)
-          sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
-          val tmp0_group = %composer.cache(false) {
-            0
-          }
-          %composer.endReplaceableGroup()
-          tmp0_group
+    if (%default and 0b0001 != 0) {
+      a = {
+        %composer.startReplaceableGroup(<>)
+        sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+        val tmp0_group = %composer.cache(false) {
+          0
         }
-        %dirty = %dirty and 0b1110.inv()
-      }
-    } else {
-      %composer.skipToGroupEnd()
-      if (%default and 0b0001 != 0) {
-        %dirty = %dirty and 0b1110.inv()
+        %composer.endReplaceableGroup()
+        tmp0_group
       }
     }
-    %composer.endDefaults()
     if (isTraceInProgress()) {
       traceEventStart(<>, %dirty, -1, <>)
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberExpressionMeta\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberExpressionMeta\133useFir = false\135.txt"
new file mode 100644
index 0000000..d3280e7
--- /dev/null
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberExpressionMeta\133useFir = false\135.txt"
@@ -0,0 +1,47 @@
+//
+// Source
+// ------------------------------------------
+
+import androidx.compose.runtime.*
+
+@Composable fun Test(param: String) {
+    val a = remember { param }
+    Test(a)
+}
+
+//
+// Transformed IR
+// ------------------------------------------
+
+@Composable
+fun Test(param: String, %composer: Composer?, %changed: Int) {
+  %composer = %composer.startRestartGroup(<>)
+  sourceInformation(%composer, "C(Test),:Test.kt")
+  val %dirty = %changed
+  if (%changed and 0b1110 == 0) {
+    %dirty = %dirty or if (%composer.changed(param)) 0b0100 else 0b0010
+  }
+  if (%dirty and 0b1011 != 0b0010 || !%composer.skipping) {
+    if (isTraceInProgress()) {
+      traceEventStart(<>, %dirty, -1, <>)
+    }
+    val a = {
+      %composer.startReplaceableGroup(<>)
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(false) {
+        param
+      }
+      %composer.endReplaceableGroup()
+      tmp0_group
+    }
+    Test(a, %composer, 0b0110)
+    if (isTraceInProgress()) {
+      traceEventEnd()
+    }
+  } else {
+    %composer.skipToGroupEnd()
+  }
+  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+    Test(param, %composer, updateChangedFlags(%changed or 0b0001))
+  }
+}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberExpressionMeta\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberExpressionMeta\133useFir = true\135.txt"
new file mode 100644
index 0000000..d3280e7
--- /dev/null
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.RememberIntrinsicTransformTests/testRememberExpressionMeta\133useFir = true\135.txt"
@@ -0,0 +1,47 @@
+//
+// Source
+// ------------------------------------------
+
+import androidx.compose.runtime.*
+
+@Composable fun Test(param: String) {
+    val a = remember { param }
+    Test(a)
+}
+
+//
+// Transformed IR
+// ------------------------------------------
+
+@Composable
+fun Test(param: String, %composer: Composer?, %changed: Int) {
+  %composer = %composer.startRestartGroup(<>)
+  sourceInformation(%composer, "C(Test),:Test.kt")
+  val %dirty = %changed
+  if (%changed and 0b1110 == 0) {
+    %dirty = %dirty or if (%composer.changed(param)) 0b0100 else 0b0010
+  }
+  if (%dirty and 0b1011 != 0b0010 || !%composer.skipping) {
+    if (isTraceInProgress()) {
+      traceEventStart(<>, %dirty, -1, <>)
+    }
+    val a = {
+      %composer.startReplaceableGroup(<>)
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(false) {
+        param
+      }
+      %composer.endReplaceableGroup()
+      tmp0_group
+    }
+    Test(a, %composer, 0b0110)
+    if (isTraceInProgress()) {
+      traceEventEnd()
+    }
+  } else {
+    %composer.skipToGroupEnd()
+  }
+  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+    Test(param, %composer, updateChangedFlags(%changed or 0b0001))
+  }
+}
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownStable\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownStable\133useFir = false\135.txt"
index 55c7278..1b894a8 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownStable\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownStable\133useFir = false\135.txt"
@@ -32,9 +32,15 @@
     }
     A(x, %composer, 0b1110 and %dirty)
     A(Foo(0), %composer, 0)
-    A(remember({
-      Foo(0)
-    }, %composer, 0), %composer, 0b0110)
+    A({
+      %composer.startReplaceableGroup(<>)
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(false) {
+        Foo(0)
+      }
+      %composer.endReplaceableGroup()
+      tmp0_group
+    }, %composer, 0b0110)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownStable\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownStable\133useFir = true\135.txt"
index 55c7278..1b894a8 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownStable\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownStable\133useFir = true\135.txt"
@@ -32,9 +32,15 @@
     }
     A(x, %composer, 0b1110 and %dirty)
     A(Foo(0), %composer, 0)
-    A(remember({
-      Foo(0)
-    }, %composer, 0), %composer, 0b0110)
+    A({
+      %composer.startReplaceableGroup(<>)
+      sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+      val tmp0_group = %composer.cache(false) {
+        Foo(0)
+      }
+      %composer.endReplaceableGroup()
+      tmp0_group
+    }, %composer, 0b0110)
     if (isTraceInProgress()) {
       traceEventEnd()
     }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownUnstable\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownUnstable\133useFir = false\135.txt"
index 129c1cd..6248fde 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownUnstable\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownUnstable\133useFir = false\135.txt"
@@ -27,9 +27,15 @@
   }
   A(x, %composer, 0b1000)
   A(Foo(0), %composer, 0b1000)
-  A(remember({
-    Foo(0)
-  }, %composer, 0), %composer, 0b1000)
+  A({
+    %composer.startReplaceableGroup(<>)
+    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    val tmp0_group = %composer.cache(false) {
+      Foo(0)
+    }
+    %composer.endReplaceableGroup()
+    tmp0_group
+  }, %composer, 0b1000)
   if (isTraceInProgress()) {
     traceEventEnd()
   }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownUnstable\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownUnstable\133useFir = true\135.txt"
index 129c1cd..6248fde 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownUnstable\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StabilityPropagationTransformTests/testPassingLocalKnownUnstable\133useFir = true\135.txt"
@@ -27,9 +27,15 @@
   }
   A(x, %composer, 0b1000)
   A(Foo(0), %composer, 0b1000)
-  A(remember({
-    Foo(0)
-  }, %composer, 0), %composer, 0b1000)
+  A({
+    %composer.startReplaceableGroup(<>)
+    sourceInformation(%composer, "CC(remember):Test.kt#9igjgp")
+    val tmp0_group = %composer.cache(false) {
+      Foo(0)
+    }
+    %composer.endReplaceableGroup()
+    tmp0_group
+  }, %composer, 0b1000)
   if (isTraceInProgress()) {
     traceEventEnd()
   }
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false\135.txt"
index 9a80ea1..ce69dc9 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = false\135.txt"
@@ -53,13 +53,13 @@
     }, %composer, 0b0110)
     TestMemoizedFun({
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changed(capture)) {
+      val tmp0_group = %composer.cache(false) {
         TestFunInterface { it: Int ->
           use(capture)
         }
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true\135.txt"
index 9a80ea1..ce69dc9 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testFunctionInterfaceMemorized\133useFir = true\135.txt"
@@ -53,13 +53,13 @@
     }, %composer, 0b0110)
     TestMemoizedFun({
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changed(capture)) {
+      val tmp0_group = %composer.cache(false) {
         TestFunInterface { it: Int ->
           use(capture)
         }
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }, %composer, 0)
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false\135.txt"
index 994ca87..34d44a4 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = false\135.txt"
@@ -32,14 +32,14 @@
     val bar = Bar(1)
     val lambda = {
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
+      val tmp0_group = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
         {
           foo
           bar
         }
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true\135.txt"
index 994ca87..34d44a4 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingStableAndUnstableCapturesInLambda\133useFir = true\135.txt"
@@ -32,14 +32,14 @@
     val bar = Bar(1)
     val lambda = {
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
+      val tmp0_group = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
         {
           foo
           bar
         }
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false\135.txt"
index a399ea5..1684495 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = false\135.txt"
@@ -27,13 +27,13 @@
     val foo = Foo(0)
     val lambda = {
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changedInstance(foo)) {
+      val tmp0_group = %composer.cache(%composer.changedInstance(foo)) {
         {
           foo
         }
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true\135.txt"
index a399ea5..1684495 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableCapturesInLambda\133useFir = true\135.txt"
@@ -27,13 +27,13 @@
     val foo = Foo(0)
     val lambda = {
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changedInstance(foo)) {
+      val tmp0_group = %composer.cache(%composer.changedInstance(foo)) {
         {
           foo
         }
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false\135.txt"
index ddced50..ad46339 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = false\135.txt"
@@ -35,14 +35,14 @@
     }
     val lambda = {
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
+      val tmp0_group = %composer.cache(%composer.changedInstance(foo) or %dirty and 0b01110000 == 0b00100000) {
         {
           foo
           bar
         }
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true\135.txt"
index ddced50..ad46339 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testMemoizingUnstableFunctionParameterInLambda\133useFir = true\135.txt"
@@ -35,14 +35,14 @@
     }
     val lambda = {
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changedInstance(foo) or %composer.changed(bar)) {
+      val tmp0_group = %composer.cache(%composer.changedInstance(foo) or %dirty and 0b01110000 == 0b00100000) {
         {
           foo
           bar
         }
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false\135.txt"
index 046e728..32f8d7f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = false\135.txt"
@@ -26,11 +26,11 @@
     val x = {
       val tmp0 = unstable
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changedInstance(tmp0)) {
+      val tmp0_group = %composer.cache(%composer.changedInstance(tmp0)) {
         tmp0::method
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true\135.txt"
index 046e728..32f8d7f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableExtensionReceiverFunctionReferenceMemoized\133useFir = true\135.txt"
@@ -26,11 +26,11 @@
     val x = {
       val tmp0 = unstable
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changedInstance(tmp0)) {
+      val tmp0_group = %composer.cache(%composer.changedInstance(tmp0)) {
         tmp0::method
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = false\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = false\135.txt"
index 046e728..32f8d7f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = false\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = false\135.txt"
@@ -26,11 +26,11 @@
     val x = {
       val tmp0 = unstable
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changedInstance(tmp0)) {
+      val tmp0_group = %composer.cache(%composer.changedInstance(tmp0)) {
         tmp0::method
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = true\135.txt" "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = true\135.txt"
index 046e728..32f8d7f 100644
--- "a/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = true\135.txt"
+++ "b/compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.StrongSkippingModeTransformTests/testUnstableReceiverFunctionReferenceMemoized\133useFir = true\135.txt"
@@ -26,11 +26,11 @@
     val x = {
       val tmp0 = unstable
       %composer.startReplaceableGroup(<>)
-      val tmpCache = %composer.cache(%composer.changedInstance(tmp0)) {
+      val tmp0_group = %composer.cache(%composer.changedInstance(tmp0)) {
         tmp0::method
       }
       %composer.endReplaceableGroup()
-      tmpCache
+      tmp0_group
     }
     if (isTraceInProgress()) {
       traceEventEnd()
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
index 202303f..df1ebf9 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
@@ -386,7 +386,7 @@
             )
             val intrinsicRememberEnabled = configuration.get(
                 ComposeConfiguration.INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_KEY,
-                false
+                true
             )
             val decoysEnabled = configuration.getBoolean(
                 ComposeConfiguration.DECOYS_ENABLED_KEY,
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposeWritableSlices.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposeWritableSlices.kt
index e2b36a9..b669a2e 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposeWritableSlices.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposeWritableSlices.kt
@@ -14,6 +14,8 @@
         BasicWritableSlice(RewritePolicy.DO_NOTHING)
     val IS_STATIC_FUNCTION_EXPRESSION: WritableSlice =
         BasicWritableSlice(RewritePolicy.DO_NOTHING)
+    val IS_STATIC_EXPRESSION: WritableSlice =
+        BasicWritableSlice(RewritePolicy.DO_NOTHING)
     val IS_COMPOSABLE_SINGLETON: WritableSlice =
         BasicWritableSlice(RewritePolicy.DO_NOTHING)
     val IS_COMPOSABLE_SINGLETON_CLASS: WritableSlice =
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
index 57add22..f7a2f24 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
@@ -60,6 +60,7 @@
 import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
 import org.jetbrains.kotlin.ir.declarations.inlineClassRepresentation
 import org.jetbrains.kotlin.ir.declarations.name
+import org.jetbrains.kotlin.ir.expressions.IrBlock
 import org.jetbrains.kotlin.ir.expressions.IrBranch
 import org.jetbrains.kotlin.ir.expressions.IrCall
 import org.jetbrains.kotlin.ir.expressions.IrConst
@@ -128,7 +129,6 @@
 import org.jetbrains.kotlin.ir.util.getArgumentsWithIr
 import org.jetbrains.kotlin.ir.util.getPropertyGetter
 import org.jetbrains.kotlin.ir.util.hasAnnotation
-import org.jetbrains.kotlin.ir.util.isFalseConst
 import org.jetbrains.kotlin.ir.util.isFunction
 import org.jetbrains.kotlin.ir.util.kotlinFqName
 import org.jetbrains.kotlin.ir.util.parentAsClass
@@ -931,6 +931,11 @@
                 // K2 sometimes produces `IrGetField` for reads from constant properties
                 symbol.owner.correspondingPropertySymbol?.owner?.isConst == true
 
+            is IrBlock -> {
+                // Check the slice in case the block was generated as expression
+                // (e.g. inlined intrinsic remember call)
+                context.irTrace[ComposeWritableSlices.IS_STATIC_EXPRESSION, this] ?: false
+            }
             else -> false
         }
     }
@@ -1036,12 +1041,6 @@
                     ) {
                         return true
                     }
-                } else if (fqName == ComposeFqNames.cache) {
-                    // If it is a call to cache then it is a transformed intrinsic call to
-                    // remember and we need to
-                    return valueArgumentsCount == 2 &&
-                        getValueArgument(0)?.isFalseConst() == true &&
-                        stabilityInferencer.stabilityOf(type).knownStable()
                 } else if (fqName == ComposeFqNames.composableLambda) {
                     // calls to this function are generated by the compiler, and this
                     // function behaves similar to a remember call in that the result will
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
index 3889c26..c18ad5c 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
@@ -3092,6 +3092,13 @@
                 before = listOf(irStartReplaceableGroup(expression, blockScope)),
                 after = listOf(irEndReplaceableGroup(scope = blockScope))
             )
+        }.also { block ->
+            if (
+                stabilityInferencer.stabilityOf(block.type).knownStable() &&
+                    inputArgMetas.all { it.isStatic }
+            ) {
+                context.irTrace.record(ComposeWritableSlices.IS_STATIC_EXPRESSION, block, true)
+            }
         }
     }
 
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index 8002e42..73fdc49 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -538,11 +538,11 @@
 package androidx.compose.foundation.gestures.snapping {
 
   public final class LazyGridSnapLayoutInfoProviderKt {
-    method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.grid.LazyGridState lazyGridState, optional androidx.compose.foundation.gestures.snapping.SnapPositionInLayout positionInLayout);
+    method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.grid.LazyGridState lazyGridState, optional androidx.compose.foundation.gestures.snapping.SnapPosition snapPosition);
   }
 
   public final class LazyListSnapLayoutInfoProviderKt {
-    method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.LazyListState lazyListState, optional androidx.compose.foundation.gestures.snapping.SnapPositionInLayout positionInLayout);
+    method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.LazyListState lazyListState, optional androidx.compose.foundation.gestures.snapping.SnapPosition snapPosition);
     method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.FlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.lazy.LazyListState lazyListState);
   }
 
@@ -562,14 +562,18 @@
     method public float calculateSnappingOffset(float currentVelocity);
   }
 
-  @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public fun interface SnapPositionInLayout {
+  @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public fun interface SnapPosition {
     method public int position(int layoutSize, int itemSize, int beforeContentPadding, int afterContentPadding, int itemIndex);
-    field public static final androidx.compose.foundation.gestures.snapping.SnapPositionInLayout.Companion Companion;
+    field public static final androidx.compose.foundation.gestures.snapping.SnapPosition.Companion Companion;
   }
 
-  public static final class SnapPositionInLayout.Companion {
-    method public androidx.compose.foundation.gestures.snapping.SnapPositionInLayout getCenterToCenter();
-    property public final androidx.compose.foundation.gestures.snapping.SnapPositionInLayout CenterToCenter;
+  public static final class SnapPosition.Companion {
+    method public androidx.compose.foundation.gestures.snapping.SnapPosition getCenter();
+    method public androidx.compose.foundation.gestures.snapping.SnapPosition getEnd();
+    method public androidx.compose.foundation.gestures.snapping.SnapPosition getStart();
+    property public final androidx.compose.foundation.gestures.snapping.SnapPosition Center;
+    property public final androidx.compose.foundation.gestures.snapping.SnapPosition End;
+    property public final androidx.compose.foundation.gestures.snapping.SnapPosition Start;
   }
 
 }
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index e61fd32..4bc8b44 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -540,11 +540,11 @@
 package androidx.compose.foundation.gestures.snapping {
 
   public final class LazyGridSnapLayoutInfoProviderKt {
-    method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.grid.LazyGridState lazyGridState, optional androidx.compose.foundation.gestures.snapping.SnapPositionInLayout positionInLayout);
+    method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.grid.LazyGridState lazyGridState, optional androidx.compose.foundation.gestures.snapping.SnapPosition snapPosition);
   }
 
   public final class LazyListSnapLayoutInfoProviderKt {
-    method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.LazyListState lazyListState, optional androidx.compose.foundation.gestures.snapping.SnapPositionInLayout positionInLayout);
+    method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.LazyListState lazyListState, optional androidx.compose.foundation.gestures.snapping.SnapPosition snapPosition);
     method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.FlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.lazy.LazyListState lazyListState);
   }
 
@@ -564,14 +564,18 @@
     method public float calculateSnappingOffset(float currentVelocity);
   }
 
-  @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public fun interface SnapPositionInLayout {
+  @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public fun interface SnapPosition {
     method public int position(int layoutSize, int itemSize, int beforeContentPadding, int afterContentPadding, int itemIndex);
-    field public static final androidx.compose.foundation.gestures.snapping.SnapPositionInLayout.Companion Companion;
+    field public static final androidx.compose.foundation.gestures.snapping.SnapPosition.Companion Companion;
   }
 
-  public static final class SnapPositionInLayout.Companion {
-    method public androidx.compose.foundation.gestures.snapping.SnapPositionInLayout getCenterToCenter();
-    property public final androidx.compose.foundation.gestures.snapping.SnapPositionInLayout CenterToCenter;
+  public static final class SnapPosition.Companion {
+    method public androidx.compose.foundation.gestures.snapping.SnapPosition getCenter();
+    method public androidx.compose.foundation.gestures.snapping.SnapPosition getEnd();
+    method public androidx.compose.foundation.gestures.snapping.SnapPosition getStart();
+    property public final androidx.compose.foundation.gestures.snapping.SnapPosition Center;
+    property public final androidx.compose.foundation.gestures.snapping.SnapPosition End;
+    property public final androidx.compose.foundation.gestures.snapping.SnapPosition Start;
   }
 
 }
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/LazyListSnappingDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/LazyListSnappingDemos.kt
index b35a1e1..cb7d7b9 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/LazyListSnappingDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/LazyListSnappingDemos.kt
@@ -19,17 +19,36 @@
 import androidx.compose.animation.core.DecayAnimationSpec
 import androidx.compose.animation.rememberSplineBasedDecay
 import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.background
 import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
+import androidx.compose.foundation.gestures.snapping.SnapPosition
 import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.lazy.LazyListState
 import androidx.compose.foundation.lazy.rememberLazyListState
 import androidx.compose.integration.demos.common.ComposableDemo
+import androidx.compose.integration.demos.common.DemoCategory
+import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
 import androidx.compose.ui.util.fastSumBy
 
+@OptIn(ExperimentalFoundationApi::class)
+val SnapPositionDemos = listOf(
+    ComposableDemo("Center") { SnapPosition(SnapPosition.Center) },
+    ComposableDemo("Start") { SnapPosition(SnapPosition.Start) },
+    ComposableDemo("End") { SnapPosition(SnapPosition.End) },
+)
+
 val LazyListSnappingDemos = listOf(
     ComposableDemo("Single Item - Same Size Items") { SameItemSizeDemo() },
     ComposableDemo("Single Item - Different Size Item") { DifferentItemSizeDemo() },
@@ -37,6 +56,7 @@
     ComposableDemo("Single Item - List with Content padding") { DifferentContentPaddingDemo() },
     ComposableDemo("Multi Item - Decayed Snapping") { DecayedSnappingDemo() },
     ComposableDemo("Multi Item - View Port Based Offset") { ViewPortBasedSnappingDemo() },
+    DemoCategory("Snap Position", SnapPositionDemos)
 )
 
 /**
@@ -44,6 +64,36 @@
  */
 @OptIn(ExperimentalFoundationApi::class)
 @Composable
+private fun SnapPosition(snapPosition: SnapPosition) {
+    val lazyListState = rememberLazyListState()
+    val layoutInfoProvider = rememberNextItemSnappingLayoutInfoProvider(lazyListState, snapPosition)
+    val flingBehavior = rememberSnapFlingBehavior(layoutInfoProvider)
+
+    SnappingDemoMainLayout(
+        lazyListState = lazyListState,
+        flingBehavior = flingBehavior
+    ) { position ->
+        Box(
+            modifier = Modifier
+                .size(150.dp)
+                .padding(8.dp)
+                .background(Color.White)
+                .drawWithContent {
+                    drawContent()
+                    drawAnchor(CenterAnchor)
+                },
+            contentAlignment = Alignment.Center
+        ) {
+            Text(text = position.toString(), fontSize = 40.sp)
+        }
+    }
+}
+
+/**
+ * Snapping happens to the next item and items have the same size
+ */
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
 private fun SameItemSizeDemo() {
     val lazyListState = rememberLazyListState()
     val layoutInfoProvider = rememberNextItemSnappingLayoutInfoProvider(lazyListState)
@@ -146,10 +196,12 @@
 @OptIn(ExperimentalFoundationApi::class)
 @Composable
 private fun rememberNextItemSnappingLayoutInfoProvider(
-    state: LazyListState
+    state: LazyListState,
+    snapPosition: SnapPosition = SnapPosition.Center
 ): SnapLayoutInfoProvider {
-    return remember(state) {
-        val basedSnappingLayoutInfoProvider = SnapLayoutInfoProvider(lazyListState = state)
+    return remember(state, snapPosition) {
+        val basedSnappingLayoutInfoProvider =
+            SnapLayoutInfoProvider(lazyListState = state, snapPosition = snapPosition)
         object : SnapLayoutInfoProvider by basedSnappingLayoutInfoProvider {
             override fun calculateApproachOffset(initialVelocity: Float): Float {
                 return 0f
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/SnappingDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/SnappingDemos.kt
index 5c8886d..8c0be81 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/SnappingDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/SnappingDemos.kt
@@ -48,9 +48,7 @@
     DemoCategory("Lazy List Snapping", LazyListSnappingDemos),
     DemoCategory("Scrollable Row Snapping", RowSnappingDemos),
     DemoCategory("Lazy Grid Snapping", LazyGridSnappingDemos),
-    ComposableDemo("Non Item based Snapping") {
-        NonItemBasedLayout()
-    },
+    ComposableDemo("Non Item based Snapping") { NonItemBasedLayout() },
 )
 
 @Composable
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapFlingBehaviorTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapFlingBehaviorTest.kt
index 73fb165..d2f6048 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapFlingBehaviorTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapFlingBehaviorTest.kt
@@ -20,7 +20,7 @@
 import androidx.compose.foundation.background
 import androidx.compose.foundation.gestures.FlingBehavior
 import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.gestures.snapping.SnapPositionInLayout.Companion.CenterToCenter
+import androidx.compose.foundation.gestures.snapping.SnapPosition.Companion.Center
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.size
@@ -469,7 +469,7 @@
                 itemSize = it.sizeOnMainAxis(orientation = layoutInfo.orientation),
                 itemOffset = it.offsetOnMainAxis(orientation = layoutInfo.orientation),
                 itemIndex = it.index,
-                snapPositionInLayout = CenterToCenter
+                snapPosition = Center
             )
             if (abs(distance) < minDistance) {
                 minDistance = abs(distance)
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapFlingBehaviorTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapFlingBehaviorTest.kt
index 86e7c46..d673ba4 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapFlingBehaviorTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapFlingBehaviorTest.kt
@@ -20,7 +20,7 @@
 import androidx.compose.foundation.gestures.FlingBehavior
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.gestures.ScrollScope
-import androidx.compose.foundation.gestures.snapping.SnapPositionInLayout.Companion.CenterToCenter
+import androidx.compose.foundation.gestures.snapping.SnapPosition.Companion.Center
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.size
@@ -454,7 +454,7 @@
                 itemSize = it.size,
                 itemOffset = it.offset,
                 itemIndex = it.index,
-                snapPositionInLayout = CenterToCenter
+                snapPosition = Center
             )
             if (abs(distance) < minDistance) {
                 minDistance = abs(distance)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapLayoutInfoProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapLayoutInfoProvider.kt
index 9ec2f75..61b1eb3 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapLayoutInfoProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyGridSnapLayoutInfoProvider.kt
@@ -35,7 +35,7 @@
  * A [SnapLayoutInfoProvider] for LazyGrids.
  *
  * @param lazyGridState The [LazyGridState] with information about the current state of the grid
- * @param positionInLayout The desired positioning of the snapped item within the main layout.
+ * @param snapPosition The desired positioning of the snapped item within the main layout.
  * This position should be considered with regards to the start edge of the item and the placement
  * within the viewport.
  *
@@ -44,7 +44,7 @@
 @ExperimentalFoundationApi
 fun SnapLayoutInfoProvider(
     lazyGridState: LazyGridState,
-    positionInLayout: SnapPositionInLayout = SnapPositionInLayout.CenterToCenter
+    snapPosition: SnapPosition = SnapPosition.Center
 ) = object : SnapLayoutInfoProvider {
     private val layoutInfo: LazyGridLayoutInfo
         get() = lazyGridState.layoutInfo
@@ -93,7 +93,7 @@
                     itemSize = item.sizeOnMainAxis(orientation = layoutInfo.orientation),
                     itemOffset = item.offsetOnMainAxis(orientation = layoutInfo.orientation),
                     itemIndex = item.index,
-                    snapPositionInLayout = positionInLayout
+                    snapPosition = snapPosition
                 )
 
             // Find item that is closest to the center
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
index 7b760c9..37cc73e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
@@ -37,7 +37,7 @@
  * A [SnapLayoutInfoProvider] for LazyLists.
  *
  * @param lazyListState The [LazyListState] with information about the current state of the list
- * @param positionInLayout The desired positioning of the snapped item within the main layout.
+ * @param snapPosition The desired positioning of the snapped item within the main layout.
  * This position should be considered with regard to the start edge of the item and the placement
  * within the viewport.
  *
@@ -46,7 +46,7 @@
 @ExperimentalFoundationApi
 fun SnapLayoutInfoProvider(
     lazyListState: LazyListState,
-    positionInLayout: SnapPositionInLayout = SnapPositionInLayout.CenterToCenter
+    snapPosition: SnapPosition = SnapPosition.Center
 ): SnapLayoutInfoProvider = object : SnapLayoutInfoProvider {
 
     private val layoutInfo: LazyListLayoutInfo
@@ -84,7 +84,7 @@
                     itemSize = item.size,
                     itemOffset = item.offset,
                     itemIndex = item.index,
-                    snapPositionInLayout = positionInLayout
+                    snapPosition = snapPosition
                 )
 
             // Find item that is closest to the center
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapPosition.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapPosition.kt
new file mode 100644
index 0000000..41f3a17
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapPosition.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.gestures.snapping
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+
+/**
+ * Describes the general positioning of a given snap item in its containing layout.
+ */
+@ExperimentalFoundationApi
+fun interface SnapPosition {
+    /**
+     * Calculates the anchor reference position where items will be snapped to in a snapping
+     * container. For instance, if [SnapPosition.Center] is used, once the snapping finishes
+     * one of the items in the snapping container will be aligned exactly to the position
+     * returned by [position]. The value returned will be applied to the item's current offset
+     * to generate its final positioning.
+     *
+     * The reference point is with respect to the start of the layout (including the content
+     * padding).
+     *
+     * @param layoutSize The main axis layout size within which an item can be positioned.
+     * @param itemSize The main axis size for the item being positioned within this snapping
+     * layout.
+     * @param beforeContentPadding The content padding in pixels applied before this Layout's
+     * content.
+     * @param afterContentPadding The content padding in pixels applied after this Layout's
+     * content.
+     * @param itemIndex The index of the item being positioned.
+     */
+    fun position(
+        layoutSize: Int,
+        itemSize: Int,
+        beforeContentPadding: Int,
+        afterContentPadding: Int,
+        itemIndex: Int
+    ): Int
+
+    companion object {
+        /**
+         * Aligns the center of the item with the center of the containing layout.
+         */
+        val Center =
+            SnapPosition { layoutSize, itemSize, beforeContentPadding, afterContentPadding, _ ->
+                val availableLayoutSpace = layoutSize - beforeContentPadding - afterContentPadding
+                // we use availableLayoutSpace / 2 as the main anchor point and we discount half
+                // an item size so the item appear aligned with the center of the container.
+                availableLayoutSpace / 2 - itemSize / 2
+            }
+
+        /**
+         * Aligns the start of the item with the start of the containing layout.
+         */
+        val Start = SnapPosition { _, _, _, _, _ -> 0 }
+
+        /**
+         * Aligns the end of the item with the end of the containing layout.
+         */
+        val End =
+            SnapPosition { layoutSize, itemSize, beforeContentPadding, afterContentPadding, _ ->
+                val availableLayoutSpace = layoutSize - beforeContentPadding - afterContentPadding
+                // the snap position for the item is the end of the layout, discounting the item
+                // size
+                availableLayoutSpace - itemSize
+            }
+    }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+internal fun calculateDistanceToDesiredSnapPosition(
+    mainAxisViewPortSize: Int,
+    beforeContentPadding: Int,
+    afterContentPadding: Int,
+    itemSize: Int,
+    itemOffset: Int,
+    itemIndex: Int,
+    snapPosition: SnapPosition
+): Float {
+    val desiredDistance = with(snapPosition) {
+        position(
+            mainAxisViewPortSize,
+            itemSize,
+            beforeContentPadding,
+            afterContentPadding,
+            itemIndex,
+        )
+    }.toFloat()
+
+    return itemOffset - desiredDistance
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapPositionInLayout.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapPositionInLayout.kt
deleted file mode 100644
index beb33b0..0000000
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapPositionInLayout.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.gestures.snapping
-
-import androidx.compose.foundation.ExperimentalFoundationApi
-
-/**
- * Describes the general positioning of a given snap item in its containing layout.
- */
-@ExperimentalFoundationApi
-fun interface SnapPositionInLayout {
-    /**
-     * Calculates an offset positioning between a container and an element within this container.
-     * The offset calculation is the necessary diff that should be applied to the item offset to
-     * align the item with a position within the container. As a base line, if we wanted to align
-     * the start of the container and the start of the item, we would return 0 in this function.
-     *
-     * @param layoutSize The main axis layout size within which an item can be positioned.
-     * @param itemSize The main axis size for the item being positioned within this snapping
-     * layout.
-     * @param beforeContentPadding The content padding in pixels applied before this Layout's
-     * content.
-     * @param afterContentPadding The content padding in pixels applied after this Layout's
-     * content.
-     * @param itemIndex The index of the item being positioned.
-     */
-    fun position(
-        layoutSize: Int,
-        itemSize: Int,
-        beforeContentPadding: Int,
-        afterContentPadding: Int,
-        itemIndex: Int
-    ): Int
-
-    companion object {
-        /**
-         * Aligns the center of the item with the center of the containing layout.
-         */
-        val CenterToCenter =
-            SnapPositionInLayout {
-                layoutSize, itemSize, beforeContentPadding, afterContentPadding, _ ->
-                val availableLayoutSpace = layoutSize - beforeContentPadding - afterContentPadding
-                availableLayoutSpace / 2 - itemSize / 2
-            }
-    }
-}
-
-@OptIn(ExperimentalFoundationApi::class)
-internal fun calculateDistanceToDesiredSnapPosition(
-    mainAxisViewPortSize: Int,
-    beforeContentPadding: Int,
-    afterContentPadding: Int,
-    itemSize: Int,
-    itemOffset: Int,
-    itemIndex: Int,
-    snapPositionInLayout: SnapPositionInLayout
-): Float {
-    val desiredDistance = with(snapPositionInLayout) {
-        position(
-            mainAxisViewPortSize,
-            itemSize,
-            beforeContentPadding,
-            afterContentPadding,
-            itemIndex,
-        )
-    }.toFloat()
-
-    return itemOffset - desiredDistance
-}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
index f95876c..840031e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
@@ -118,7 +118,7 @@
         horizontalAlignment = horizontalAlignment,
         verticalAlignment = verticalAlignment,
         itemProviderLambda = pagerItemProvider,
-        snapPositionInLayout = SnapAlignmentStartToStart,
+        snapPosition = SnapAlignmentStartToStart,
         pageCount = { state.pageCount }
     )
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
index 7145a86..5161445 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
@@ -751,7 +751,7 @@
                     itemSize = layoutInfo.pageSize,
                     itemOffset = currentOffset,
                     itemIndex = page,
-                    snapPositionInLayout = SnapAlignmentStartToStart
+                    snapPosition = SnapAlignmentStartToStart
                 )
 
                 debugLog { "Snapping Offset=$offset for page=$page" }
@@ -784,7 +784,7 @@
                     itemSize = layoutInfo.pageSize,
                     itemOffset = currentOffset,
                     itemIndex = page,
-                    snapPositionInLayout = SnapAlignmentStartToStart
+                    snapPosition = SnapAlignmentStartToStart
                 )
 
                 debugLog {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt
index 0ba1da9..02bd345 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt
@@ -18,7 +18,7 @@
 
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.gestures.snapping.SnapPositionInLayout
+import androidx.compose.foundation.gestures.snapping.SnapPosition
 import androidx.compose.foundation.gestures.snapping.calculateDistanceToDesiredSnapPosition
 import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy
 import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
@@ -55,7 +55,7 @@
     pageAvailableSize: Int,
     beyondBoundsPageCount: Int,
     pinnedPages: List,
-    snapPositionInLayout: SnapPositionInLayout,
+    snapPosition: SnapPosition,
     layout: (Int, Int, Placeable.PlacementScope.() -> Unit) -> MeasureResult
 ): PagerMeasureResult {
     require(beforeContentPadding >= 0) { "negative beforeContentPadding" }
@@ -368,7 +368,7 @@
                 beforeContentPadding,
                 afterContentPadding,
                 pageSizeWithSpacing,
-                snapPositionInLayout
+                snapPosition
             )
 
         val currentPagePositionOffset = newCurrentPage?.offset ?: 0
@@ -464,7 +464,7 @@
     beforeContentPadding: Int,
     afterContentPadding: Int,
     itemSize: Int,
-    snapPositionInLayout: SnapPositionInLayout
+    snapPosition: SnapPosition
 ): MeasuredPage? {
     return visiblePagesInfo.fastMaxBy {
         -abs(
@@ -475,7 +475,7 @@
                 itemSize = itemSize,
                 itemOffset = it.offset,
                 itemIndex = it.index,
-                snapPositionInLayout = snapPositionInLayout
+                snapPosition = snapPosition
             )
         )
     }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasurePolicy.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasurePolicy.kt
index 6f30f0f..35bbf3b 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasurePolicy.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasurePolicy.kt
@@ -19,7 +19,7 @@
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.checkScrollableContainerConstraints
 import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.gestures.snapping.SnapPositionInLayout
+import androidx.compose.foundation.gestures.snapping.SnapPosition
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.calculateEndPadding
 import androidx.compose.foundation.layout.calculateStartPadding
@@ -52,7 +52,7 @@
     pageSize: PageSize,
     horizontalAlignment: Alignment.Horizontal?,
     verticalAlignment: Alignment.Vertical?,
-    snapPositionInLayout: SnapPositionInLayout,
+    snapPosition: SnapPosition,
     pageCount: () -> Int,
 ) = remember MeasureResult>(
     state,
@@ -63,7 +63,7 @@
     verticalAlignment,
     pageSpacing,
     pageSize,
-    snapPositionInLayout,
+    snapPosition,
     pageCount,
 ) {
     { containerConstraints ->
@@ -174,7 +174,7 @@
             pagerItemProvider = itemProvider,
             reverseLayout = reverseLayout,
             pinnedPages = pinnedPages,
-            snapPositionInLayout = snapPositionInLayout,
+            snapPosition = snapPosition,
             layout = { width, height, placement ->
                 state.remeasureTrigger // read state to trigger remeasures on state write
                 layout(
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
index 9e67b52..ad2a58a 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
@@ -24,7 +24,7 @@
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.gestures.ScrollScope
 import androidx.compose.foundation.gestures.ScrollableState
-import androidx.compose.foundation.gestures.snapping.SnapPositionInLayout
+import androidx.compose.foundation.gestures.snapping.SnapPosition
 import androidx.compose.foundation.interaction.InteractionSource
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.lazy.layout.AwaitFirstLayoutModifier
@@ -728,7 +728,7 @@
 
 @OptIn(ExperimentalFoundationApi::class)
 internal val SnapAlignmentStartToStart =
-    SnapPositionInLayout { _, _, _, _, _ -> 0 }
+    SnapPosition { _, _, _, _, _ -> 0 }
 
 private const val DEBUG = PagerDebugEnable
 private inline fun debugLog(generateMsg: () -> String) {
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DerivedState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DerivedState.kt
index 18e36e8..82266d1 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DerivedState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/DerivedState.kt
@@ -226,15 +226,11 @@
             ) {
                 readable.dependencies = newDependencies
                 readable.resultHash = readable.readableHash(this, currentSnapshot)
-                readable.validSnapshotId = snapshot.id
-                readable.validSnapshotWriteCount = snapshot.writeCount
                 readable
             } else {
                 val writable = first.newWritableRecord(this, currentSnapshot)
                 writable.dependencies = newDependencies
                 writable.resultHash = writable.readableHash(this, currentSnapshot)
-                writable.validSnapshotId = snapshot.id
-                writable.validSnapshotWriteCount = snapshot.writeCount
                 writable.result = result
                 writable
             }
@@ -242,6 +238,12 @@
 
         if (calculationBlockNestedLevel.get()?.element == 0) {
             Snapshot.notifyObjectsInitialized()
+
+            sync {
+                val currentSnapshot = Snapshot.current
+                record.validSnapshotId = currentSnapshot.id
+                record.validSnapshotWriteCount = currentSnapshot.writeCount
+            }
         }
 
         return record
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMap.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMap.kt
deleted file mode 100644
index f1f4022..0000000
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMap.kt
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.runtime.collection
-
-import androidx.compose.runtime.identityHashCode
-
-internal class IdentityArrayIntMap {
-    var size = 0
-        private set
-    var keys: Array = arrayOfNulls(4)
-        private set
-    var values: IntArray = IntArray(4)
-        private set
-
-    operator fun get(key: Any): Int {
-        val index = find(key)
-        return if (index >= 0) values[index] else error("Key not found")
-    }
-    /**
-     * Add [value] to the map and return `-1` if it was added or previous value if it already existed.
-     */
-    fun add(key: Any, value: Int): Int {
-        val values = values
-
-        val index: Int
-        if (size > 0) {
-            index = find(key)
-            if (index >= 0) {
-                val previousValue = values[index]
-                values[index] = value
-                return previousValue
-            }
-        } else {
-            index = -1
-        }
-
-        val insertIndex = -(index + 1)
-
-        val keys = keys
-        val size = size
-        if (size == keys.size) {
-            val newKeys = arrayOfNulls(keys.size * 2)
-            val newValues = IntArray(keys.size * 2)
-            keys.copyInto(
-                destination = newKeys,
-                destinationOffset = insertIndex + 1,
-                startIndex = insertIndex,
-                endIndex = size
-            )
-            values.copyInto(
-                destination = newValues,
-                destinationOffset = insertIndex + 1,
-                startIndex = insertIndex,
-                endIndex = size
-            )
-            keys.copyInto(
-                destination = newKeys,
-                endIndex = insertIndex
-            )
-            values.copyInto(
-                destination = newValues,
-                endIndex = insertIndex
-            )
-            this.keys = newKeys
-            this.values = newValues
-        } else {
-            keys.copyInto(
-                destination = keys,
-                destinationOffset = insertIndex + 1,
-                startIndex = insertIndex,
-                endIndex = size
-            )
-            values.copyInto(
-                destination = values,
-                destinationOffset = insertIndex + 1,
-                startIndex = insertIndex,
-                endIndex = size
-            )
-        }
-        this.keys[insertIndex] = key
-        this.values[insertIndex] = value
-        this.size++
-
-        return -1
-    }
-
-    /**
-     * Remove [key] from the map.
-     */
-    fun remove(key: Any): Boolean {
-        val index = find(key)
-
-        val keys = keys
-        val values = values
-        val size = size
-        if (index >= 0) {
-            if (index < size - 1) {
-                keys.copyInto(
-                    destination = keys,
-                    destinationOffset = index,
-                    startIndex = index + 1,
-                    endIndex = size
-                )
-                values.copyInto(
-                    destination = values,
-                    destinationOffset = index,
-                    startIndex = index + 1,
-                    endIndex = size
-                )
-            }
-            val newSize = size - 1
-            keys[newSize] = null
-            this.size = newSize
-            return true
-        }
-        return false
-    }
-
-    /**
-     * Removes all values that match [predicate].
-     */
-    inline fun removeValueIf(predicate: (Any, Int) -> Boolean) {
-        val keys = keys
-        val values = values
-        val size = size
-
-        var destinationIndex = 0
-        for (i in 0 until size) {
-            @Suppress("UNCHECKED_CAST")
-            val key = keys[i] as Any
-            val value = values[i]
-            if (!predicate(key, value)) {
-                if (destinationIndex != i) {
-                    keys[destinationIndex] = key
-                    values[destinationIndex] = value
-                }
-                destinationIndex++
-            }
-        }
-        for (i in destinationIndex until size) {
-            keys[i] = null
-        }
-        this.size = destinationIndex
-    }
-
-    inline fun any(predicate: (Any, Int) -> Boolean): Boolean {
-        val keys = keys
-        val values = values
-        val size = size
-
-        for (i in 0 until size) {
-            if (predicate(keys[i] as Any, values[i])) return true
-        }
-        return false
-    }
-
-    inline fun forEach(block: (Any, Int) -> Unit) {
-        val keys = keys
-        val values = values
-        val size = size
-
-        for (i in 0 until size) {
-            block(keys[i] as Any, values[i])
-        }
-    }
-
-    /**
-     * Returns the index of [key] in the set or the negative index - 1 of the location where
-     * it would have been if it had been in the set.
-     */
-    private fun find(key: Any?): Int {
-        var low = 0
-        var high = size - 1
-        val valueIdentity = identityHashCode(key)
-
-        val keys = keys
-        while (low <= high) {
-            val mid = (low + high).ushr(1)
-            val midVal = keys[mid]
-            val midIdentity = identityHashCode(midVal)
-            when {
-                midIdentity < valueIdentity -> low = mid + 1
-                midIdentity > valueIdentity -> high = mid - 1
-                midVal === key -> return mid
-                else -> return findExactIndex(mid, key, valueIdentity)
-            }
-        }
-        return -(low + 1)
-    }
-
-    /**
-     * When multiple items share the same [identityHashCode], then we must find the specific
-     * index of the target item. This method assumes that [midIndex] has already been checked
-     * for an exact match for [value], but will look at nearby values to find the exact item index.
-     * If no match is found, the negative index - 1 of the position in which it would be will
-     * be returned, which is always after the last item with the same [identityHashCode].
-     */
-    private fun findExactIndex(midIndex: Int, value: Any?, valueHash: Int): Int {
-        val keys = keys
-        val size = size
-
-        // hunt down first
-        for (i in midIndex - 1 downTo 0) {
-            val v = keys[i]
-            if (v === value) {
-                return i
-            }
-            if (identityHashCode(v) != valueHash) {
-                break // we've gone too far
-            }
-        }
-
-        for (i in midIndex + 1 until size) {
-            val v = keys[i]
-            if (v === value) {
-                return i
-            }
-            if (identityHashCode(v) != valueHash) {
-                // We've gone too far. We should insert here.
-                return -(i + 1)
-            }
-        }
-
-        // We should insert at the end
-        return -(size + 1)
-    }
-}
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt
index 0cf05d1..65aa75c 100644
--- a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt
+++ b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/GroupSizeValidationTests.kt
@@ -75,8 +75,8 @@
     fun checkboxLike() = compositionTest {
         slotExpect(
             name = "CheckboxLike",
-            noMoreGroupsThan = 11,
-            noMoreSlotsThan = 21
+            noMoreGroupsThan = 12,
+            noMoreSlotsThan = 17
         ) {
             CheckboxLike(checked = false, onCheckedChange = { })
         }
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMapTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMapTests.kt
deleted file mode 100644
index 95bbc05..0000000
--- a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/collection/IdentityArrayIntMapTests.kt
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.runtime.collection
-
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import kotlin.test.assertFalse
-import kotlin.test.assertTrue
-
-class IdentityArrayIntMapTests {
-
-    @Test
-    fun emptyConstruction() {
-        val m = IdentityArrayIntMap()
-        assertEquals(0, m.size)
-    }
-
-    @Test
-    fun canAddValues() {
-        val map = IdentityArrayIntMap()
-        val keys = Array(100) { Any() }
-        for (i in keys.indices) {
-            map.add(keys[i], i)
-        }
-        for (i in keys.indices) {
-            assertEquals(i, map[keys[i]])
-        }
-        map.removeValueIf { key, value ->
-            assertEquals(keys[value], key)
-            false
-        }
-    }
-
-    @Test
-    fun addReturnsWhetherValueWasAdded() {
-        val map = IdentityArrayIntMap()
-        val key1 = Any()
-
-        assertEquals(-1, map.add(key1, 0))
-        assertEquals(0, map.add(key1, 0))
-        assertEquals(0, map.add(key1, 1))
-    }
-
-    @Test
-    fun canRemoveValues() {
-        val map = IdentityArrayIntMap()
-        val keys = Array(100) { Any() }
-        for (i in keys.indices) {
-            map.add(keys[i], i)
-        }
-        for (i in keys.indices step 2) {
-            map.remove(keys[i])
-        }
-        assertEquals(50, map.size)
-        map.removeValueIf { key, value ->
-            assertEquals(keys[value], key)
-            assertTrue(value % 2 == 1)
-            false
-        }
-    }
-
-    @Test
-    fun canRemoveIfValues() {
-        val map = IdentityArrayIntMap()
-        val keys = Array(100) { Any() }
-        for (i in keys.indices) {
-            map.add(keys[i], i)
-        }
-        map.removeValueIf { _, value -> value % 2 == 0 }
-        assertEquals(50, map.size)
-    }
-
-    @Test
-    fun canReplaceValues() {
-        val map = IdentityArrayIntMap()
-        val keys = Array(100) { Any() }
-        for (i in keys.indices) {
-            map.add(keys[i], i)
-        }
-
-        for (i in keys.indices) {
-            map.add(keys[i], i + 100)
-        }
-
-        assertEquals(100, map.size)
-        for (i in keys.indices) {
-            assertEquals(i + 100, map[keys[i]])
-        }
-    }
-
-    @Test
-    fun anyFindsCorrectValue() {
-        val map = IdentityArrayIntMap()
-        val keys = Array(100) { Any() }
-        for (i in keys.indices) {
-            map.add(keys[i], i)
-        }
-        assertTrue(map.any { _, value -> value == 20 })
-        assertFalse(map.any { _, value -> value > 100 })
-    }
-
-    @Test
-    fun canForEach() {
-        val map = IdentityArrayIntMap()
-        val keys = Array(100) { Any() }
-        for (i in keys.indices) {
-            map.add(keys[i], i)
-        }
-        map.forEach { key, value ->
-            assertEquals(keys.indexOf(key), value)
-        }
-    }
-}
diff --git a/compose/ui/ui-graphics/build.gradle b/compose/ui/ui-graphics/build.gradle
index 4e8d5c3..784adda 100644
--- a/compose/ui/ui-graphics/build.gradle
+++ b/compose/ui/ui-graphics/build.gradle
@@ -65,7 +65,9 @@
         androidMain {
             dependsOn(jvmMain)
             dependencies {
-                implementation("androidx.graphics:graphics-path:1.0.0-alpha02")
+                //TODO: Switch to pinned version when beta1 is released as it fixes bugs
+                //implementation("androidx.graphics:graphics-path:1.0.0-beta01")
+                implementation(project(":graphics:graphics-path"))
                 api(libs.androidx.annotation)
             }
         }
diff --git a/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/ParagraphWithLineHeightBenchmark.kt b/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/ParagraphWithLineHeightBenchmark.kt
index 113a102..9bda1d2 100644
--- a/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/ParagraphWithLineHeightBenchmark.kt
+++ b/compose/ui/ui-text/benchmark/src/androidTest/java/androidx/compose/ui/text/benchmark/ParagraphWithLineHeightBenchmark.kt
@@ -43,21 +43,19 @@
 class ParagraphWithLineHeightBenchmark(
     private val textLength: Int,
     private val addNewLine: Boolean,
-    private val applyLineHeight: Boolean,
-    private val lineHeightStyle: LineHeightStyle?
+    private val applyLineHeight: Boolean
 ) {
     companion object {
         @JvmStatic
         @Parameterized.Parameters(
-            name = "length={0} newLine={1} applyLineHeight={2} lineHeightStyle={3}"
+            name = "length={0} newLine={1} applyLineHeight={2}"
         )
         fun initParameters(): List> = cartesian(
             arrayOf(16),
             // add new line
             arrayOf(true),
             // apply line height
-            arrayOf(false, true),
-            arrayOf(LineHeightStyle.Default)
+            arrayOf(false, true)
         )
     }
 
@@ -103,13 +101,13 @@
             TextStyle(
                 fontSize = fontSize,
                 lineHeight = fontSize * 2,
-                lineHeightStyle = lineHeightStyle,
+                lineHeightStyle = LineHeightStyle.Default,
                 platformStyle = PlatformTextStyle(includeFontPadding = false)
             )
         } else {
             TextStyle(
                 fontSize = fontSize,
-                lineHeightStyle = lineHeightStyle,
+                lineHeightStyle = LineHeightStyle.Default,
                 platformStyle = PlatformTextStyle(includeFontPadding = false)
             )
         }
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/focus/DeactivatedFocusNodeTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/focus/DeactivatedFocusNodeTest.kt
new file mode 100644
index 0000000..cdc42ef
--- /dev/null
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/focus/DeactivatedFocusNodeTest.kt
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.focus
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusStateImpl.Inactive
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import org.junit.Rule
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeactivatedFocusNodeTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    private lateinit var lazyListState: LazyListState
+    private lateinit var coroutineScope: CoroutineScope
+    private val focusStates = mutableMapOf()
+    private val initialFocusedItem = FocusRequester()
+
+    @Test
+    fun deactivatedActiveFocusNodeSendsFocusEvent() {
+        // Arrange.
+        rule.setTestContent {
+            LazyRow(
+                state = lazyListState,
+                modifier = Modifier.size(10.dp)
+            ) {
+                items(2) { index ->
+                    Box(
+                        Modifier
+                            .size(10.dp)
+                            .testTag("$index")
+                            .then(
+                                if (index == 0) {
+                                    Modifier.focusRequester(initialFocusedItem)
+                                } else {
+                                    Modifier
+                                }
+                            )
+                            .onFocusChanged { focusStates[index] = it }
+                            .focusTarget()
+                    )
+                }
+            }
+        }
+        rule.runOnIdle {
+            initialFocusedItem.requestFocus()
+            focusStates.clear()
+        }
+
+        // Act.
+        rule.runOnIdle {
+            coroutineScope.launch { lazyListState.scrollToItem(1) }
+        }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(focusStates[0]).isEqualTo(Inactive)
+        }
+    }
+
+    @Test
+    fun deactivatedActiveParentFocusNodeSendsFocusEvent() {
+        // Arrange.
+        rule.setTestContent {
+            LazyRow(
+                state = lazyListState,
+                modifier = Modifier.size(10.dp)
+            ) {
+                items(2) { index ->
+                    Box(
+                        Modifier
+                            .size(10.dp)
+                            .onFocusChanged { focusStates[index] = it }
+                            .focusTarget()
+                    ) {
+                        Box(
+                            Modifier
+                                .size(5.dp)
+                                .then(
+                                    if (index == 0) {
+                                        Modifier.focusRequester(initialFocusedItem)
+                                    } else {
+                                        Modifier
+                                    }
+                                )
+                                .focusTarget()
+
+                        )
+                    }
+                }
+            }
+        }
+        rule.runOnIdle {
+            initialFocusedItem.requestFocus()
+            focusStates.clear()
+        }
+
+        // Act.
+        rule.runOnIdle {
+            coroutineScope.launch { lazyListState.scrollToItem(1) }
+        }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(focusStates[0]).isEqualTo(Inactive)
+        }
+    }
+
+    @Test
+    fun deactivatedCapturedFocusNodeSendsFocusEvent() {
+        // Arrange.
+        rule.setTestContent {
+            LazyRow(
+                state = lazyListState,
+                modifier = Modifier.size(10.dp)
+            ) {
+                items(2) { index ->
+                    Box(
+                        Modifier
+                            .size(10.dp)
+                            .testTag("$index")
+                            .then(
+                                if (index == 0) {
+                                    Modifier.focusRequester(initialFocusedItem)
+                                } else {
+                                    Modifier
+                                }
+                            )
+                            .onFocusChanged { focusStates[index] = it }
+                            .focusTarget()
+                    )
+                }
+            }
+        }
+        rule.runOnIdle {
+            initialFocusedItem.requestFocus()
+            initialFocusedItem.captureFocus()
+            focusStates.clear()
+        }
+
+        // Act.
+        rule.runOnIdle {
+            coroutineScope.launch { lazyListState.scrollToItem(1) }
+        }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(focusStates[0]).isEqualTo(Inactive)
+        }
+    }
+
+    @Test
+    fun deactivatedInactiveFocusNodeDoesNotSendFocusEvent() {
+        // Arrange.
+        rule.setTestContent {
+            LazyRow(
+                state = lazyListState,
+                modifier = Modifier.size(10.dp)
+            ) {
+                items(2) { index ->
+                    Box(
+                        Modifier
+                            .size(10.dp)
+                            .testTag("$index")
+                            .then(
+                                if (index == 0) {
+                                    Modifier.focusRequester(initialFocusedItem)
+                                } else {
+                                    Modifier
+                                }
+                            )
+                            .onFocusChanged { focusStates[index] = it }
+                            .focusTarget()
+                    )
+                }
+            }
+        }
+        rule.runOnIdle {
+            focusStates.clear()
+        }
+
+        // Act.
+        rule.runOnIdle {
+            coroutineScope.launch { lazyListState.scrollToItem(1) }
+        }
+
+        // Assert.
+        rule.runOnIdle {
+            assertThat(focusStates[0]).isNull()
+        }
+    }
+
+    private fun ComposeContentTestRule.setTestContent(content: @Composable () -> Unit) {
+        setContent {
+            coroutineScope = rememberCoroutineScope()
+            lazyListState = rememberLazyListState()
+            Box { content() }
+        }
+    }
+}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusInvalidationManager.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusInvalidationManager.kt
index d838abf..9843404f 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusInvalidationManager.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusInvalidationManager.kt
@@ -44,17 +44,23 @@
         focusPropertiesNodes.scheduleInvalidation(node)
     }
 
+    fun hasPendingInvalidation(): Boolean {
+        return focusTargetNodes.isNotEmpty() ||
+        focusPropertiesNodes.isNotEmpty() ||
+        focusEventNodes.isNotEmpty()
+    }
+
     private fun  MutableSet.scheduleInvalidation(node: T) {
         if (add(node)) {
             // If this is the first node scheduled for invalidation,
             // we set up a listener that runs after onApplyChanges.
             if (focusTargetNodes.size + focusEventNodes.size + focusPropertiesNodes.size == 1) {
-                onRequestApplyChangesListener.invoke(invalidateNodes)
+                onRequestApplyChangesListener.invoke(::invalidateNodes)
             }
         }
     }
 
-    private val invalidateNodes: () -> Unit = {
+    private fun invalidateNodes() {
         // Process all the invalidated FocusProperties nodes.
         focusPropertiesNodes.forEach {
             // We don't need to invalidate a focus properties node if it was scheduled for
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwnerImpl.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwnerImpl.kt
index 8572a48..0a7515b 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwnerImpl.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusOwnerImpl.kt
@@ -200,6 +200,10 @@
      * Dispatches a key event through the compose hierarchy.
      */
     override fun dispatchKeyEvent(keyEvent: KeyEvent): Boolean {
+        check(!focusInvalidationManager.hasPendingInvalidation()) {
+            "Dispatching key event while focus system is invalidated."
+        }
+
         if (!validateKeyEvent(keyEvent)) return false
 
         val activeFocusTarget = rootFocusNode.findActiveFocusNode()
@@ -219,6 +223,10 @@
 
     @OptIn(ExperimentalComposeUiApi::class)
     override fun dispatchInterceptedSoftKeyboardEvent(keyEvent: KeyEvent): Boolean {
+        check(!focusInvalidationManager.hasPendingInvalidation()) {
+            "Dispatching intercepted soft keyboard event while focus system is invalidated."
+        }
+
         val focusedSoftKeyboardInterceptionNode = rootFocusNode.findActiveFocusNode()
             ?.nearestAncestor(Nodes.SoftKeyboardKeyInput)
 
@@ -234,6 +242,10 @@
      * Dispatches a rotary scroll event through the compose hierarchy.
      */
     override fun dispatchRotaryEvent(event: RotaryScrollEvent): Boolean {
+        check(!focusInvalidationManager.hasPendingInvalidation()) {
+            "Dispatching rotary event while focus system is invalidated."
+        }
+
         val focusedRotaryInputNode = rootFocusNode.findActiveFocusNode()
             ?.nearestAncestor(Nodes.RotaryInput)
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetNode.kt
index 70597a9..ea6ba20 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTargetNode.kt
@@ -78,12 +78,19 @@
      * Clears focus if this focus target has it.
      */
     override fun onReset() {
+        //  Note: onReset() is called after onEndApplyChanges, so we can't schedule any nodes for
+        //  invalidation here. If we do, they will be run on the next onEndApplyChanges.
         when (focusState) {
             // Clear focus from the current FocusTarget.
             // This currently clears focus from the entire hierarchy, but we can change the
             // implementation so that focus is sent to the immediate focus parent.
             Active, Captured -> requireOwner().focusOwner.clearFocus(force = true)
-            ActiveParent, Inactive -> scheduleInvalidationForFocusEvents()
+
+            // If an ActiveParent is deactivated, the entire subtree containing focus is
+            // deactivated, which means the Active node will also receive an onReset() call.
+            // This triggers a clearFocus call, which will notify all the focus event nodes
+            // associated with this FocusTargetNode.
+            ActiveParent, Inactive -> {}
         }
         // This node might be reused, so we reset its state.
         committedFocusState = null
@@ -185,16 +192,14 @@
     }
 
     internal fun scheduleInvalidationForFocusEvents() {
-        // include possibility for ourselves to also be a focus event modifier node in case
-        // we are being delegated to
-        node.dispatchForKind(Nodes.FocusEvent) { eventNode ->
-            eventNode.invalidateFocusEvent()
-        }
         // Since this is potentially called while _this_ node is getting detached, it is possible
         // that the nodes above us are already detached, thus, we check for isAttached here.
         // We should investigate changing the order that children.detach() is called relative to
         // actually nulling out / detaching ones self.
-        visitAncestors(Nodes.FocusEvent or Nodes.FocusTarget) {
+        visitAncestors(
+            mask = Nodes.FocusEvent or Nodes.FocusTarget,
+            includeSelf = true
+        ) {
             if (it.isKind(Nodes.FocusTarget)) return@visitAncestors
 
             if (it.isAttached) {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/TwoDimensionalFocusSearch.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/TwoDimensionalFocusSearch.kt
index c8be093..987cc9a 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/TwoDimensionalFocusSearch.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/TwoDimensionalFocusSearch.kt
@@ -30,13 +30,12 @@
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.node.DelegatableNode
 import androidx.compose.ui.node.Nodes
+import androidx.compose.ui.node.requireLayoutNode
 import androidx.compose.ui.node.visitChildren
 import kotlin.math.absoluteValue
 import kotlin.math.max
 
-@Suppress("ConstPropertyName")
 private const val InvalidFocusDirection = "This function should only be used for 2-D focus search"
-@Suppress("ConstPropertyName")
 private const val NoActiveChild = "ActiveParent must have a focusedChild"
 
 /**
@@ -189,7 +188,7 @@
 ) {
     visitChildren(Nodes.FocusTarget) {
         // TODO(b/278765590): Find the root issue why visitChildren returns unattached nodes.
-        if (!it.isAttached) return@visitChildren
+        if (!it.isAttached || it.requireLayoutNode().isDeactivated) return@visitChildren
 
         if (it.fetchFocusProperties().canFocus) {
             accessibleChildren.add(it)
diff --git a/wear/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/wear/compose/integration/macrobenchmark/target/ScrollActivity.kt b/wear/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/wear/compose/integration/macrobenchmark/target/ScrollActivity.kt
index c2e4a1b..13d0551 100644
--- a/wear/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/wear/compose/integration/macrobenchmark/target/ScrollActivity.kt
+++ b/wear/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/wear/compose/integration/macrobenchmark/target/ScrollActivity.kt
@@ -37,7 +37,7 @@
 import androidx.wear.compose.material.Text
 
 class ScrollActivity : ComponentActivity() {
-    private var itemHeightDp: Dp = 20.dp
+    private var itemHeightDp: Dp = 60.dp
     private var defaultItemSpacingDp: Dp = 8.dp
 
     override fun onCreate(savedInstanceState: Bundle?) {
@@ -62,7 +62,11 @@
                                 .background(MaterialTheme.colors.surface)
                                 .fillMaxSize()
                         ) {
-                            Text(text = "Item $it", color = MaterialTheme.colors.onSurface)
+                            Text(
+                                modifier = Modifier.align(Alignment.Center),
+                                text = "Item $it",
+                                color = MaterialTheme.colors.onSurface
+                            )
                         }
                     }
                 }