KT-15862 Inline generic functions can unexpectedly box primitives
Previous version of the boxing/unboxing analysis treated merging boxed and non-boxed values as a hazard.
If such merged values are not used (e.g., early return + local variables reused in inlined calls),
corresponding boxing/unboxing operations still can be optimized out.
All information related to boxed value usage by instructions is moved to 'BoxedValueDescriptor'.
Introduce "tainted" (and "clean") boxed values, with the following rules:
merge(B, B) = B, if unboxed types are compatible,
T, otherwise
merge(B, X) = T
merge(T, X) = T
where
X is a non-boxed value,
B is a "clean" boxed value,
T is a "tainted" boxed value.
Postpone decision about value merge hazards until a "tainted" value is used.
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
// FILE: test.kt
|
||||
|
||||
// @TestKt.class:
|
||||
// 0 valueOf
|
||||
// 0 Value\s\(\)
|
||||
|
||||
val mask = 127
|
||||
val entries = IntArray(128)
|
||||
val flags = BooleanArray(128)
|
||||
|
||||
fun distance(index: Int, hash: Int): Int = (128 + index - (hash and mask)) and mask
|
||||
|
||||
fun insertSad(x: Int): Boolean {
|
||||
return insertWithBoxing(
|
||||
x,
|
||||
hash = { it },
|
||||
equals = { a, b -> a == b },
|
||||
isEmpty = { !flags[it] },
|
||||
fetch = { entries[it] },
|
||||
store = { i, x -> entries[i] = x; flags[i] = true; }
|
||||
)
|
||||
}
|
||||
|
||||
// FILE: inline.kt
|
||||
inline fun <T> insertWithBoxing(entry: T,
|
||||
hash: (T) -> Int,
|
||||
equals: (T, T) -> Boolean,
|
||||
isEmpty: (Int) -> Boolean,
|
||||
fetch: (Int) -> T,
|
||||
store: (Int, T) -> Unit): Boolean {
|
||||
var currentEntry = entry
|
||||
var index = hash(entry) and mask
|
||||
var dist = 0
|
||||
do {
|
||||
if (isEmpty(index)) {
|
||||
store(index, currentEntry)
|
||||
return true
|
||||
}
|
||||
|
||||
val existingEntry = fetch(index)
|
||||
if (equals(existingEntry, currentEntry)) {
|
||||
return false
|
||||
}
|
||||
|
||||
val existingHash = hash(existingEntry)
|
||||
val existingDistance = distance(index, existingHash)
|
||||
if (existingDistance < dist) {
|
||||
store(index, currentEntry)
|
||||
currentEntry = existingEntry
|
||||
dist = existingDistance
|
||||
}
|
||||
|
||||
dist += 1
|
||||
index = (index + 1) and mask
|
||||
}
|
||||
while (true)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// FILE: test.kt
|
||||
|
||||
// @TestKt.class:
|
||||
// 0 valueOf
|
||||
// 0 Value\s\(\)
|
||||
|
||||
const val SIZE = 16
|
||||
val arr = IntArray(SIZE) { -1 }
|
||||
|
||||
fun putNonNegInt(x: Int) =
|
||||
put(x, SIZE,
|
||||
isEmpty = { arr[it] < 0 },
|
||||
equals = { x, y -> x == y },
|
||||
fetch = { arr[it] },
|
||||
store = { i, x -> arr[i] = x }
|
||||
)
|
||||
|
||||
// FILE: inline.kt
|
||||
inline fun <T> put(
|
||||
x: T,
|
||||
maxExclusive: Int,
|
||||
isEmpty: (Int) -> Boolean,
|
||||
equals: (T, T) -> Boolean,
|
||||
fetch: (Int) -> T,
|
||||
store: (Int, T) -> Unit
|
||||
): Boolean {
|
||||
var i = 0
|
||||
do {
|
||||
if (isEmpty(i)) {
|
||||
store(i, x)
|
||||
return true
|
||||
}
|
||||
|
||||
val y = fetch(i)
|
||||
if (equals(x, y)) {
|
||||
return false
|
||||
}
|
||||
|
||||
i++
|
||||
if (i >= maxExclusive) return false
|
||||
} while (true)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user