[K/N] Normalize on nan values in code generator

^KT-56041
This commit is contained in:
Pavel Kunyavskiy
2023-07-27 14:37:34 +02:00
committed by Space Team
parent 6417f6456f
commit 858f432807
6 changed files with 21 additions and 13 deletions
@@ -15,7 +15,6 @@ import org.jetbrains.kotlin.platform.TargetPlatform
* 'true' - interpreter will construct object and initialize its properties despite the fact it is not marked as compile time;
* 'false' - interpreter will create a representation of empty object, that can be used to get const properties
* @param inlineConstVal tell the interpreter that value of const property can be inlined instead of getter call
* @param inlineNanVal allows to inline values of `Double.NaN` and `Float.NaN`
*/
// TODO maybe create some sort of builder
data class IrInterpreterConfiguration(
@@ -26,5 +25,4 @@ data class IrInterpreterConfiguration(
val printOnlyExceptionMessage: Boolean = false,
val collapseStackTraceFromJDK: Boolean = true,
val inlineConstVal: Boolean = true,
val inlineNanVal: Boolean = true,
)
@@ -122,9 +122,6 @@ class IrInterpreterCommonChecker : IrInterpreterChecker {
}
override fun visitConst(expression: IrConst<*>, data: IrInterpreterCheckerData): Boolean {
if (!data.interpreterConfiguration.inlineNanVal) {
return !expression.isNaN()
}
return true
}
@@ -489,9 +489,7 @@ private val objectClassesPhase = createFileLoweringPhase(
private val constEvaluationPhase = createFileLoweringPhase(
lowering = { context: Context ->
// NaN constants has inconsistencies between IR and metadata representation,
// so inlining them can lead to incorrect behaviour. Check KT-53258 for details.
val configuration = IrInterpreterConfiguration(printOnlyExceptionMessage = true, inlineNanVal = false)
val configuration = IrInterpreterConfiguration(printOnlyExceptionMessage = true)
ConstEvaluationLowering(context, configuration = configuration)
},
name = "ConstEvaluationLowering",
@@ -1843,6 +1843,22 @@ internal class CodeGeneratorVisitor(
private fun evaluateStringConst(value: IrConst<String>) =
codegen.staticData.kotlinStringLiteral(value.value)
/**
* Normalizing nans to single value is useful for build reproducibility.
*
* It's possible that it can lead to some bad consequences for interop libraries,
* for which exact nan value is important. We are not aware of the existence of
* any such useful library, at least on priority targets.
*
* On the other side, the semantics of exact cases, NaN values should be not normalized, is unclear.
* E.g., in previous implementation, storing constant to another constant could change the exact bit pattern.
*
* So for now, we would just normalize all NaN constants. At least this leads to predictable result
* useful in almost all cases.
*/
private fun Float.normalizeNan() = if (isNaN()) Float.NaN else this
private fun Double.normalizeNan() = if (isNaN()) Double.NaN else this
private fun evaluateConst(value: IrConst<*>): ConstValue {
context.log{"evaluateConst : ${ir2string(value)}"}
/* This suppression against IrConst<String> */
@@ -1856,8 +1872,8 @@ internal class CodeGeneratorVisitor(
IrConstKind.Int -> llvm.constInt32(value.value as Int)
IrConstKind.Long -> llvm.constInt64(value.value as Long)
IrConstKind.String -> evaluateStringConst(value as IrConst<String>)
IrConstKind.Float -> llvm.constFloat32(value.value as Float)
IrConstKind.Double -> llvm.constFloat64(value.value as Double)
IrConstKind.Float -> llvm.constFloat32((value.value as Float).normalizeNan())
IrConstKind.Double -> llvm.constFloat64((value.value as Double).normalizeNan())
}
}
@@ -4298,7 +4298,6 @@ interopTest("interop_globals") {
}
interopTest("interop_macros") {
disabled = isK2(project) // KT-56041
source = "interop/basics/macros.kt"
interop = 'cmacros'
}
@@ -60,7 +60,7 @@ fun main(args: Array<String>) {
assertEquals(doubleNanBase, 0x7ff8000000000000L)
assertEquals(floatNanBase, DEFAULT_FLOAT_NAN.toRawBits())
assertEquals(doubleNanBase, DEFAULT_DOUBLE_NAN.toRawBits())
assertEquals(floatNanBase or 0x12345, OTHER_FLOAT_NAN.toRawBits())
assertEquals(doubleNanBase or 0x123456789abL, OTHER_DOUBLE_NAN.toRawBits())
assertEquals(floatNanBase, OTHER_FLOAT_NAN.toRawBits())
assertEquals(doubleNanBase, OTHER_DOUBLE_NAN.toRawBits())
}
}