mirror of
https://github.com/fluencelabs/asmble
synced 2025-04-25 06:42:22 +00:00
Fixed float comparisons wrt NaN
This commit is contained in:
parent
2c52dcba25
commit
8afa01bede
@ -22,7 +22,7 @@ data class ClsContext(
|
|||||||
val nonAdjacentMemAccessesRequiringLocalVar: Int = 3,
|
val nonAdjacentMemAccessesRequiringLocalVar: Int = 3,
|
||||||
val eagerFailLargeMemOffset: Boolean = true,
|
val eagerFailLargeMemOffset: Boolean = true,
|
||||||
val preventMemIndexOverflow: Boolean = false,
|
val preventMemIndexOverflow: Boolean = false,
|
||||||
val preserveNanBits: Boolean = true
|
val accurateNanBits: Boolean = true
|
||||||
) : Logger by logger {
|
) : Logger by logger {
|
||||||
val importFuncs: List<Node.Import> by lazy { mod.imports.filter { it.kind is Node.Import.Kind.Func } }
|
val importFuncs: List<Node.Import> by lazy { mod.imports.filter { it.kind is Node.Import.Kind.Func } }
|
||||||
val importGlobals: List<Node.Import> by lazy { mod.imports.filter { it.kind is Node.Import.Kind.Global } }
|
val importGlobals: List<Node.Import> by lazy { mod.imports.filter { it.kind is Node.Import.Kind.Global } }
|
||||||
|
@ -203,11 +203,11 @@ open class FuncBuilder {
|
|||||||
is Node.Instr.F32Lt ->
|
is Node.Instr.F32Lt ->
|
||||||
applyF32Cmp(ctx, fn, Opcodes.IFLT)
|
applyF32Cmp(ctx, fn, Opcodes.IFLT)
|
||||||
is Node.Instr.F32Gt ->
|
is Node.Instr.F32Gt ->
|
||||||
applyF32Cmp(ctx, fn, Opcodes.IFGT)
|
applyF32Cmp(ctx, fn, Opcodes.IFGT, nanIsOne = false)
|
||||||
is Node.Instr.F32Le ->
|
is Node.Instr.F32Le ->
|
||||||
applyF32Cmp(ctx, fn, Opcodes.IFLE)
|
applyF32Cmp(ctx, fn, Opcodes.IFLE)
|
||||||
is Node.Instr.F32Ge ->
|
is Node.Instr.F32Ge ->
|
||||||
applyF32Cmp(ctx, fn, Opcodes.IFGE)
|
applyF32Cmp(ctx, fn, Opcodes.IFGE, nanIsOne = false)
|
||||||
is Node.Instr.F64Eq ->
|
is Node.Instr.F64Eq ->
|
||||||
applyF64Cmp(ctx, fn, Opcodes.IFEQ)
|
applyF64Cmp(ctx, fn, Opcodes.IFEQ)
|
||||||
is Node.Instr.F64Ne ->
|
is Node.Instr.F64Ne ->
|
||||||
@ -215,11 +215,11 @@ open class FuncBuilder {
|
|||||||
is Node.Instr.F64Lt ->
|
is Node.Instr.F64Lt ->
|
||||||
applyF64Cmp(ctx, fn, Opcodes.IFLT)
|
applyF64Cmp(ctx, fn, Opcodes.IFLT)
|
||||||
is Node.Instr.F64Gt ->
|
is Node.Instr.F64Gt ->
|
||||||
applyF64Cmp(ctx, fn, Opcodes.IFGT)
|
applyF64Cmp(ctx, fn, Opcodes.IFGT, nanIsOne = false)
|
||||||
is Node.Instr.F64Le ->
|
is Node.Instr.F64Le ->
|
||||||
applyF64Cmp(ctx, fn, Opcodes.IFLE)
|
applyF64Cmp(ctx, fn, Opcodes.IFLE)
|
||||||
is Node.Instr.F64Ge ->
|
is Node.Instr.F64Ge ->
|
||||||
applyF64Cmp(ctx, fn, Opcodes.IFGE)
|
applyF64Cmp(ctx, fn, Opcodes.IFGE, nanIsOne = false)
|
||||||
is Node.Instr.I32Clz ->
|
is Node.Instr.I32Clz ->
|
||||||
// TODO Should make unsigned?
|
// TODO Should make unsigned?
|
||||||
applyI32Unary(ctx, fn, Integer::class.invokeStatic("numberOfLeadingZeros", Int::class, Int::class))
|
applyI32Unary(ctx, fn, Integer::class.invokeStatic("numberOfLeadingZeros", Int::class, Int::class))
|
||||||
@ -298,7 +298,7 @@ open class FuncBuilder {
|
|||||||
applyI64BinarySecondOpI32(ctx, fn, java.lang.Long::class.invokeStatic("rotateRight",
|
applyI64BinarySecondOpI32(ctx, fn, java.lang.Long::class.invokeStatic("rotateRight",
|
||||||
Long::class, Long::class, Int::class))
|
Long::class, Long::class, Int::class))
|
||||||
is Node.Instr.F32Abs ->
|
is Node.Instr.F32Abs ->
|
||||||
applyF32UnaryPreserveNanBits(ctx, fn) { fn ->
|
applyF32UnaryNanReturnConst(ctx, fn) { fn ->
|
||||||
applyF32Unary(ctx, fn, forceFnType<(Float) -> Float>(Math::abs).invokeStatic())
|
applyF32Unary(ctx, fn, forceFnType<(Float) -> Float>(Math::abs).invokeStatic())
|
||||||
}
|
}
|
||||||
is Node.Instr.F32Neg ->
|
is Node.Instr.F32Neg ->
|
||||||
@ -310,7 +310,7 @@ open class FuncBuilder {
|
|||||||
is Node.Instr.F32Trunc ->
|
is Node.Instr.F32Trunc ->
|
||||||
applyF32Trunc(ctx, fn)
|
applyF32Trunc(ctx, fn)
|
||||||
is Node.Instr.F32Nearest ->
|
is Node.Instr.F32Nearest ->
|
||||||
applyF32UnaryPreserveNanBits(ctx, fn) { fn ->
|
applyF32UnaryNanReturnSame(ctx, fn) { fn ->
|
||||||
applyWithF32To64AndBack(ctx, fn) { fn -> applyF64Unary(ctx, fn, Math::rint.invokeStatic()) }
|
applyWithF32To64AndBack(ctx, fn) { fn -> applyF64Unary(ctx, fn, Math::rint.invokeStatic()) }
|
||||||
}
|
}
|
||||||
is Node.Instr.F32Sqrt ->
|
is Node.Instr.F32Sqrt ->
|
||||||
@ -330,7 +330,9 @@ open class FuncBuilder {
|
|||||||
is Node.Instr.F32CopySign ->
|
is Node.Instr.F32CopySign ->
|
||||||
applyF32Binary(ctx, fn, forceFnType<(Float, Float) -> Float>(Math::copySign).invokeStatic())
|
applyF32Binary(ctx, fn, forceFnType<(Float, Float) -> Float>(Math::copySign).invokeStatic())
|
||||||
is Node.Instr.F64Abs ->
|
is Node.Instr.F64Abs ->
|
||||||
|
applyF32UnaryNanReturnConst(ctx, fn) { fn ->
|
||||||
applyF64Unary(ctx, fn, forceFnType<(Double) -> Double>(Math::abs).invokeStatic())
|
applyF64Unary(ctx, fn, forceFnType<(Double) -> Double>(Math::abs).invokeStatic())
|
||||||
|
}
|
||||||
is Node.Instr.F64Neg ->
|
is Node.Instr.F64Neg ->
|
||||||
applyF64Unary(ctx, fn, InsnNode(Opcodes.DNEG))
|
applyF64Unary(ctx, fn, InsnNode(Opcodes.DNEG))
|
||||||
is Node.Instr.F64Ceil ->
|
is Node.Instr.F64Ceil ->
|
||||||
@ -340,7 +342,9 @@ open class FuncBuilder {
|
|||||||
is Node.Instr.F64Trunc ->
|
is Node.Instr.F64Trunc ->
|
||||||
applyF64Trunc(ctx, fn)
|
applyF64Trunc(ctx, fn)
|
||||||
is Node.Instr.F64Nearest ->
|
is Node.Instr.F64Nearest ->
|
||||||
|
applyF64UnaryNanReturnSame(ctx, fn) { fn ->
|
||||||
applyF64Unary(ctx, fn, Math::rint.invokeStatic())
|
applyF64Unary(ctx, fn, Math::rint.invokeStatic())
|
||||||
|
}
|
||||||
is Node.Instr.F64Sqrt ->
|
is Node.Instr.F64Sqrt ->
|
||||||
applyF64Unary(ctx, fn, Math::sqrt.invokeStatic())
|
applyF64Unary(ctx, fn, Math::sqrt.invokeStatic())
|
||||||
is Node.Instr.F64Add ->
|
is Node.Instr.F64Add ->
|
||||||
@ -761,8 +765,58 @@ open class FuncBuilder {
|
|||||||
).push(Float::class.ref)
|
).push(Float::class.ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun applyF32UnaryPreserveNanBits(ctx: FuncContext, fn: Func, cb: (Func) -> Func): Func {
|
fun applyF64UnaryNanReturnConst(ctx: FuncContext, fn: Func, cb: (Func) -> Func): Func {
|
||||||
if (!ctx.cls.preserveNanBits) return cb(fn)
|
if (!ctx.cls.accurateNanBits) return cb(fn)
|
||||||
|
val isNan = LabelNode()
|
||||||
|
val allDone = LabelNode()
|
||||||
|
return fn.addInsns(
|
||||||
|
InsnNode(Opcodes.DUP2), // [d, d]
|
||||||
|
InsnNode(Opcodes.DUP2), // [d, d, d]
|
||||||
|
// Equals compare to check nan
|
||||||
|
InsnNode(Opcodes.DCMPL), // [d, z]
|
||||||
|
JumpInsnNode(Opcodes.IFNE, isNan) // [d]
|
||||||
|
).let(cb).addInsns(
|
||||||
|
JumpInsnNode(Opcodes.GOTO, allDone),
|
||||||
|
isNan,
|
||||||
|
InsnNode(Opcodes.POP2),
|
||||||
|
Double.NaN.const,
|
||||||
|
allDone
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun applyF32UnaryNanReturnConst(ctx: FuncContext, fn: Func, cb: (Func) -> Func): Func {
|
||||||
|
if (!ctx.cls.accurateNanBits) return cb(fn)
|
||||||
|
val isNan = LabelNode()
|
||||||
|
val allDone = LabelNode()
|
||||||
|
return fn.addInsns(
|
||||||
|
InsnNode(Opcodes.DUP), // [f, f]
|
||||||
|
InsnNode(Opcodes.DUP), // [f, f, f]
|
||||||
|
// Equals compare to check nan
|
||||||
|
InsnNode(Opcodes.FCMPL), // [f, z]
|
||||||
|
JumpInsnNode(Opcodes.IFNE, isNan) // [f]
|
||||||
|
).let(cb).addInsns(
|
||||||
|
JumpInsnNode(Opcodes.GOTO, allDone),
|
||||||
|
isNan,
|
||||||
|
InsnNode(Opcodes.POP),
|
||||||
|
Float.NaN.const,
|
||||||
|
allDone
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun applyF64UnaryNanReturnSame(ctx: FuncContext, fn: Func, cb: (Func) -> Func): Func {
|
||||||
|
if (!ctx.cls.accurateNanBits) return cb(fn)
|
||||||
|
val allDone = LabelNode()
|
||||||
|
return fn.addInsns(
|
||||||
|
InsnNode(Opcodes.DUP2), // [d, d]
|
||||||
|
InsnNode(Opcodes.DUP2), // [d, d, d]
|
||||||
|
// Equals compare to check nan
|
||||||
|
InsnNode(Opcodes.DCMPL), // [d, z]
|
||||||
|
JumpInsnNode(Opcodes.IFNE, allDone) // [d]
|
||||||
|
).let(cb).addInsns(allDone)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun applyF32UnaryNanReturnSame(ctx: FuncContext, fn: Func, cb: (Func) -> Func): Func {
|
||||||
|
if (!ctx.cls.accurateNanBits) return cb(fn)
|
||||||
// Extra work for NaN, ref:
|
// Extra work for NaN, ref:
|
||||||
// http://stackoverflow.com/questions/43129365/javas-math-rint-not-behaving-as-expected-when-using-nan
|
// http://stackoverflow.com/questions/43129365/javas-math-rint-not-behaving-as-expected-when-using-nan
|
||||||
val allDone = LabelNode()
|
val allDone = LabelNode()
|
||||||
@ -832,19 +886,18 @@ open class FuncBuilder {
|
|||||||
fun applyUnary(ctx: FuncContext, fn: Func, type: TypeRef, insn: AbstractInsnNode) =
|
fun applyUnary(ctx: FuncContext, fn: Func, type: TypeRef, insn: AbstractInsnNode) =
|
||||||
fn.popExpecting(type).addInsns(insn).push(type)
|
fn.popExpecting(type).addInsns(insn).push(type)
|
||||||
|
|
||||||
fun applyF32Cmp(ctx: FuncContext, fn: Func, op: Int) =
|
fun applyF32Cmp(ctx: FuncContext, fn: Func, op: Int, nanIsOne: Boolean = true) =
|
||||||
|
// TODO: Can we shorten this and use the direct cmp result instead of IF<OP>?
|
||||||
fn.popExpecting(Float::class.ref).
|
fn.popExpecting(Float::class.ref).
|
||||||
popExpecting(Float::class.ref).
|
popExpecting(Float::class.ref).
|
||||||
// TODO: test whether we need FCMPG instead
|
addInsns(InsnNode(if (nanIsOne) Opcodes.FCMPG else Opcodes.FCMPL)).
|
||||||
addInsns(InsnNode(Opcodes.FCMPL)).
|
|
||||||
push(Int::class.ref).
|
push(Int::class.ref).
|
||||||
let { fn -> applyI32UnaryCmp(ctx, fn, op) }
|
let { fn -> applyI32UnaryCmp(ctx, fn, op) }
|
||||||
|
|
||||||
fun applyF64Cmp(ctx: FuncContext, fn: Func, op: Int) =
|
fun applyF64Cmp(ctx: FuncContext, fn: Func, op: Int, nanIsOne: Boolean = true) =
|
||||||
fn.popExpecting(Double::class.ref).
|
fn.popExpecting(Double::class.ref).
|
||||||
popExpecting(Double::class.ref).
|
popExpecting(Double::class.ref).
|
||||||
// TODO: test whether we need DCMPG instead
|
addInsns(InsnNode(if (nanIsOne) Opcodes.DCMPG else Opcodes.FCMPL)).
|
||||||
addInsns(InsnNode(Opcodes.DCMPL)).
|
|
||||||
push(Int::class.ref).
|
push(Int::class.ref).
|
||||||
let { fn -> applyI32UnaryCmp(ctx, fn, op) }
|
let { fn -> applyI32UnaryCmp(ctx, fn, op) }
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user