Optimize FloatFloatPair

Improves performance of FloatFloatPair by using the same technique
used in Compose to dramatically reduce the number of instructions
generated by converting int bits to floats. The default Kotlin
implementation forces a static resolution that is completely un-
necessary and expensive. With this change, the generated code
boils down to a single fmov on arm64 after JIT or AOT compilation.

This means we also greatly reduce the amount of generated code
since FloatFloatPair inlines its getters at the call site.

The Kotlin Native implementation unfortunately still suffers
from the original issue as Kotlin hides kotlin.fromBits() from
us. There isn't an easy way to fix this currently unfortunately.

RelNote: Improved performance of FloatFloatPair
Test: PairTest (jvm and native)
Change-Id: If5537d58ae9248b558bfc1c63e4fdec4f343253c
diff --git a/collection/collection/api/restricted_1.4.0-beta01.txt b/collection/collection/api/restricted_1.4.0-beta01.txt
index 2bbe99a..e9e19da 100644
--- a/collection/collection/api/restricted_1.4.0-beta01.txt
+++ b/collection/collection/api/restricted_1.4.0-beta01.txt
@@ -2187,3 +2187,11 @@
 
 }
 
+package androidx.collection.internal {
+
+  public final class PackingHelpers_jvmKt {
+    method @kotlin.PublishedApi internal static inline float floatFromBits(int bits);
+  }
+
+}
+
diff --git a/collection/collection/api/restricted_current.ignore b/collection/collection/api/restricted_current.ignore
new file mode 100644
index 0000000..627fb9c
--- /dev/null
+++ b/collection/collection/api/restricted_current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+AddedPackage: androidx.collection.internal:
+    Added package androidx.collection.internal
diff --git a/collection/collection/api/restricted_current.txt b/collection/collection/api/restricted_current.txt
index 2bbe99a..e9e19da 100644
--- a/collection/collection/api/restricted_current.txt
+++ b/collection/collection/api/restricted_current.txt
@@ -2187,3 +2187,11 @@
 
 }
 
+package androidx.collection.internal {
+
+  public final class PackingHelpers_jvmKt {
+    method @kotlin.PublishedApi internal static inline float floatFromBits(int bits);
+  }
+
+}
+
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/FloatFloatPair.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/FloatFloatPair.kt
index 602efb3..45d5574 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/FloatFloatPair.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/FloatFloatPair.kt
@@ -17,6 +17,7 @@
 
 package androidx.collection
 
+import androidx.collection.internal.floatFromBits
 import kotlin.jvm.JvmField
 import kotlin.jvm.JvmInline
 
@@ -44,13 +45,13 @@
      * The first value in the pair.
      */
     public inline val first: Float
-        get() = Float.fromBits((packedValue shr 32).toInt())
+        get() = floatFromBits((packedValue shr 32).toInt())
 
     /**
      * The second value in the pair.
      */
     public inline val second: Float
-        get() = Float.fromBits((packedValue and 0xFFFFFFFF).toInt())
+        get() = floatFromBits((packedValue and 0xFFFFFFFF).toInt())
 
     /**
      * Returns the [first] component of the pair. For instance, the first component
@@ -63,7 +64,7 @@
      * ```
      */
     // NOTE: Unpack the value directly because using `first` forces an invokestatic
-    public inline operator fun component1(): Float = Float.fromBits((packedValue shr 32).toInt())
+    public inline operator fun component1(): Float = floatFromBits((packedValue shr 32).toInt())
 
     /**
      * Returns the [second] component of the pair. For instance, the second component
@@ -77,7 +78,7 @@
      */
     // NOTE: Unpack the value directly because using `second` forces an invokestatic
     public inline operator fun component2(): Float =
-        Float.fromBits((packedValue and 0xFFFFFFFF).toInt())
+        floatFromBits((packedValue and 0xFFFFFFFF).toInt())
 
     override fun toString(): String = "($first, $second)"
 }
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/internal/PackingHelpers.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/internal/PackingHelpers.kt
new file mode 100644
index 0000000..a8c03d2
--- /dev/null
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/internal/PackingHelpers.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.collection.internal
+
+// The function below is technically identical to Float.fromBits()
+// However, since it is declared as top- level functions, it does
+// not incur the cost of a static fetch through the Companion class.
+// Using this top-level function, the generated arm64 code after
+// dex2oat is exactly a single `fmov`
+
+/**
+ * Returns the [Float] value corresponding to a given bit representation.
+ */
+@PublishedApi
+internal expect fun floatFromBits(bits: Int): Float
diff --git a/collection/collection/src/jvmMain/java/androidx/collection/internal/PackingHelpers.jvm.kt b/collection/collection/src/jvmMain/java/androidx/collection/internal/PackingHelpers.jvm.kt
new file mode 100644
index 0000000..fecf527
--- /dev/null
+++ b/collection/collection/src/jvmMain/java/androidx/collection/internal/PackingHelpers.jvm.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.collection.internal
+
+@PublishedApi
+internal actual inline fun floatFromBits(bits: Int): Float = java.lang.Float.intBitsToFloat(bits)
diff --git a/collection/collection/src/nativeMain/kotlin/androidx/collection/internal/PackingHelpers.native.kt b/collection/collection/src/nativeMain/kotlin/androidx/collection/internal/PackingHelpers.native.kt
new file mode 100644
index 0000000..658d706
--- /dev/null
+++ b/collection/collection/src/nativeMain/kotlin/androidx/collection/internal/PackingHelpers.native.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.collection.internal
+
+// Ideally we would just reach for kotlin.fromBits(bits) and bypass the companion
+// object like we do in the JVM modules, but alas...
+@PublishedApi
+internal actual inline fun floatFromBits(bits: Int): Float = Float.fromBits(bits)