Skip to content

Commit

Permalink
feat: use soft float
Browse files Browse the repository at this point in the history
  • Loading branch information
omarsy committed Nov 28, 2024
1 parent 5c6ce77 commit 4fabee4
Show file tree
Hide file tree
Showing 8 changed files with 310 additions and 163 deletions.
19 changes: 11 additions & 8 deletions gnovm/pkg/gnolang/gonative.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package gnolang

import (
"fmt"
"math"
"reflect"

"github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat"
)

// NOTE
Expand Down Expand Up @@ -379,9 +382,9 @@ func go2GnoValue(alloc *Allocator, rv reflect.Value) (tv TypedValue) {
case reflect.Uint64:
tv.SetUint64(rv.Uint())
case reflect.Float32:
tv.SetFloat32(float32(rv.Float()))
tv.SetFloat32(softfloat.Float32(math.Float32bits(float32(rv.Float()))))
case reflect.Float64:
tv.SetFloat64(rv.Float())
tv.SetFloat64(softfloat.Float64(math.Float64bits(rv.Float())))
case reflect.Array:
tv.V = alloc.NewNative(rv)
case reflect.Slice:
Expand Down Expand Up @@ -478,11 +481,11 @@ func go2GnoValueUpdate(alloc *Allocator, rlm *Realm, lvl int, tv *TypedValue, rv
}
case Float32Kind:
if lvl != 0 {
tv.SetFloat32(float32(rv.Float()))
tv.SetFloat32(softfloat.Float32(math.Float32bits(float32(rv.Float()))))
}
case Float64Kind:
if lvl != 0 {
tv.SetFloat64(rv.Float())
tv.SetFloat64(softfloat.Float64(math.Float64bits(rv.Float())))
}
case BigintKind:
panic("not yet implemented")
Expand Down Expand Up @@ -694,9 +697,9 @@ func go2GnoValue2(alloc *Allocator, store Store, rv reflect.Value, recursive boo
case reflect.Uint64:
tv.SetUint64(rv.Uint())
case reflect.Float32:
tv.SetFloat32(float32(rv.Float()))
tv.SetFloat32(softfloat.Float32(math.Float32bits(float32(rv.Float()))))
case reflect.Float64:
tv.SetFloat64(rv.Float())
tv.SetFloat64(softfloat.Float64(math.Float64bits(rv.Float())))
case reflect.Array:
rvl := rv.Len()
if rv.Type().Elem().Kind() == reflect.Uint8 {
Expand Down Expand Up @@ -1099,9 +1102,9 @@ func gno2GoValue(tv *TypedValue, rv reflect.Value) (ret reflect.Value) {
case Uint64Type:
rv.SetUint(tv.GetUint64())
case Float32Type:
rv.SetFloat(float64(tv.GetFloat32()))
rv.SetFloat(math.Float64frombits(uint64(tv.GetFloat32())))
case Float64Type:
rv.SetFloat(tv.GetFloat64())
rv.SetFloat(math.Float64frombits(uint64(tv.GetFloat64())))
default:
panic(fmt.Sprintf(
"unexpected type %s",
Expand Down
14 changes: 7 additions & 7 deletions gnovm/pkg/gnolang/internal/softfloat/runtime_softfloat64_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

114 changes: 80 additions & 34 deletions gnovm/pkg/gnolang/internal/softfloat/softfloat.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,72 @@
// ge f >= g
package softfloat

const (
mask = 0x7FF
shift = 64 - 11 - 1
bias = 1023
)

type Float64 uint64
type Float32 uint32

func Trunc(x Float64) Float64 {
_, _, _, isInf, IsNaN := funpack64(uint64(x))

Check failure on line 28 in gnovm/pkg/gnolang/internal/softfloat/softfloat.go

View workflow job for this annotation

GitHub Actions / Run Main / Go Linter / lint

declaration has 3 blank identifiers (dogsled)
if x == 0 || isInf || IsNaN {
return x
}

d, _ := Modf(x)
return d
}
func Modf(u Float64) (int Float64, frac Float64) {

Check failure on line 36 in gnovm/pkg/gnolang/internal/softfloat/softfloat.go

View workflow job for this annotation

GitHub Actions / Run Main / Go Linter / lint

named return int has same name as predeclared identifier (predeclared)
cmp, _ := Fcmp64(u, 1)

if cmp < 0 {
cmp, _ := Fcmp64(u, 0)
switch {
case cmp < 0:
int, frac = Modf(Fneg64(u))
return Fneg64(int), Fneg64(frac)
case cmp == 0:
return u, u // Return -0, -0 when f == -0
}
return 0, u
}

e := uint(u>>shift)&mask - bias

// Keep the top 12+e bits, the integer part; clear the rest.
if e < 64-12 {
u &^= 1<<(64-12-e) - 1
}

frac = Fsub64(u, int)
return
}

// This file mostly exports the functions from runtime_softfloat64.go

//go:generate sh copy.sh

func Fadd64(f, g uint64) uint64 { return fadd64(f, g) }
func Fsub64(f, g uint64) uint64 { return fsub64(f, g) }
func Fmul64(f, g uint64) uint64 { return fmul64(f, g) }
func Fdiv64(f, g uint64) uint64 { return fdiv64(f, g) }
func Fneg64(f uint64) uint64 { return fneg64(f) }
func Feq64(f, g uint64) bool { return feq64(f, g) }
func Fgt64(f, g uint64) bool { return fgt64(f, g) }
func Fge64(f, g uint64) bool { return fge64(f, g) }

func Fadd32(f, g uint32) uint32 { return fadd32(f, g) }
func Fsub32(f, g uint32) uint32 { return fadd32(f, Fneg32(g)) }
func Fmul32(f, g uint32) uint32 { return fmul32(f, g) }
func Fdiv32(f, g uint32) uint32 { return fdiv32(f, g) }
func Feq32(f, g uint32) bool { return feq32(f, g) }
func Fgt32(f, g uint32) bool { return fgt32(f, g) }
func Fge32(f, g uint32) bool { return fge32(f, g) }

func Fcmp64(f, g uint64) (cmp int32, isnan bool) { return fcmp64(f, g) }
func Fadd64(f, g Float64) Float64 { return Float64(fadd64(uint64(f), uint64(g))) }
func Fsub64(f, g Float64) Float64 { return Float64(fsub64(uint64(f), uint64(g))) }
func Fmul64(f, g Float64) Float64 { return Float64(fmul64(uint64(f), uint64(g))) }
func Fdiv64(f, g Float64) Float64 { return Float64(fdiv64(uint64(f), uint64(g))) }
func Fneg64(f Float64) Float64 { return Float64(fneg64(uint64(f))) }
func Feq64(f, g Float64) bool { return feq64(uint64(f), uint64(g)) }
func Fgt64(f, g Float64) bool { return fgt64(uint64(f), uint64(g)) }
func Fge64(f, g Float64) bool { return fge64(uint64(f), uint64(g)) }

func Fadd32(f, g Float32) Float32 { return Float32(fadd32(uint32(f), uint32(g))) }
func Fsub32(f, g Float32) Float32 { return Float32(fadd32(uint32(f), uint32(Fneg32(uint32(g))))) }

Check failure on line 76 in gnovm/pkg/gnolang/internal/softfloat/softfloat.go

View workflow job for this annotation

GitHub Actions / Run Main / Go Linter / lint

unnecessary conversion (unconvert)
func Fmul32(f, g Float32) Float32 { return Float32(fmul32(uint32(f), uint32(g))) }
func Fdiv32(f, g Float32) Float32 { return Float32(fdiv32(uint32(f), uint32(g))) }
func Feq32(f, g Float32) bool { return feq32(uint32(f), uint32(g)) }
func Fgt32(f, g Float32) bool { return fgt32(uint32(f), uint32(g)) }
func Fge32(f, g Float32) bool { return fge32(uint32(f), uint32(g)) }

func Fcmp64(f, g Float64) (cmp int32, isnan bool) { return fcmp64(uint64(f), uint64(g)) }

func Fneg32(f uint32) uint32 {
// Not defined in runtime - this is a copy similar to fneg64.
Expand All @@ -45,19 +89,21 @@ func Fneg32(f uint32) uint32 {

// Conversions

func Fintto64(val int64) (f uint64) { return fintto64(val) }
func Fintto32(val int64) (f uint32) { return fintto32(val) }

func F32to64(f uint32) uint64 { return f32to64(f) }
func F32toint32(x uint32) int32 { return f32toint32(x) }
func F32toint64(x uint32) int64 { return f32toint64(x) }
func F32touint64(x uint32) uint64 { return f32touint64(x) }
func F64to32(f uint64) uint32 { return f64to32(f) }
func F64toint(f uint64) (val int64, ok bool) { return f64toint(f) }
func F64toint32(x uint64) int32 { return f64toint32(x) }
func F64toint64(x uint64) int64 { return f64toint64(x) }
func F64touint64(x uint64) uint64 { return f64touint64(x) }
func Fint32to32(x int32) uint32 { return fint32to32(x) }
func Fint32to64(x int32) uint64 { return fint32to64(x) }
func Fint64to32(x int64) uint32 { return fint64to32(x) }
func Fint64to64(x int64) uint64 { return fint64to64(x) }
func Fintto64(val int64) Float64 { return Float64(fintto64(val)) }
func Fintto32(val int64) Float32 { return Float32(fintto32(val)) }

func F32to64(f Float32) Float64 { return Float64(f32to64(uint32(f))) }
func F32toint32(x Float32) int32 { return f32toint32(uint32(x)) }
func F32toint64(x Float32) int64 { return f32toint64(uint32(x)) }
func F32touint64(x Float32) uint64 { return f32touint64(uint32(x)) }
func F64to32(f Float64) Float32 { return Float32(f64to32(uint64(f))) }
func F64toint(f Float64) (val int64, ok bool) { return f64toint(uint64(f)) }
func F64toint32(x Float64) int32 { return f64toint32(uint64(x)) }
func F64toint64(x Float64) int64 { return f64toint64(uint64(x)) }
func F64touint64(x Float64) uint64 { return f64touint64(uint64(x)) }
func Fint32to32(x int32) Float32 { return Float32(fint32to32(x)) }
func Fint32to64(x int32) Float64 { return Float64(fint32to64(x)) }
func Fint64to32(x int64) Float32 { return Float32(fint64to32(x)) }
func Fint64to64(x int64) Float64 { return Float64(fint64to64(x)) }
func Fuint64to32(x uint64) Float32 { return Float32(fuint64to32(x)) }
func Fuint64to64(x uint64) Float64 { return Float64(fuint64to64(x)) }
53 changes: 31 additions & 22 deletions gnovm/pkg/gnolang/op_binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"math/big"

"github.com/cockroachdb/apd/v3"
"github.com/gnolang/gno/gnovm/pkg/gnolang/internal/softfloat"
)

// ----------------------------------------
Expand Down Expand Up @@ -390,9 +391,11 @@ func isEql(store Store, lv, rv *TypedValue) bool {
case Uint64Kind:
return (lv.GetUint64() == rv.GetUint64())
case Float32Kind:
return (lv.GetFloat32() == rv.GetFloat32()) // XXX determinism?
cmp, nan := softfloat.Fcmp64(softfloat.Float64(lv.GetFloat32()), softfloat.Float64(rv.GetFloat32()))
return cmp == 0 && !nan
case Float64Kind:
return (lv.GetFloat64() == rv.GetFloat64()) // XXX determinism?
cmp, nan := softfloat.Fcmp64(lv.GetFloat64(), rv.GetFloat64())
return cmp == 0 && !nan
case BigintKind:
lb := lv.V.(BigintValue).V
rb := rv.V.(BigintValue).V
Expand Down Expand Up @@ -531,9 +534,11 @@ func isLss(lv, rv *TypedValue) bool {
case Uint64Kind:
return (lv.GetUint64() < rv.GetUint64())
case Float32Kind:
return (lv.GetFloat32() < rv.GetFloat32()) // XXX determinism?
cmp, nan := softfloat.Fcmp64(softfloat.Float64(lv.GetFloat32()), softfloat.Float64(rv.GetFloat32()))
return (cmp < 0) && !nan
case Float64Kind:
return (lv.GetFloat64() < rv.GetFloat64()) // XXX determinism?
cmp, nan := softfloat.Fcmp64(lv.GetFloat64(), rv.GetFloat64())
return (cmp < 0) && !nan
case BigintKind:
lb := lv.V.(BigintValue).V
rb := rv.V.(BigintValue).V
Expand Down Expand Up @@ -575,9 +580,11 @@ func isLeq(lv, rv *TypedValue) bool {
case Uint64Kind:
return (lv.GetUint64() <= rv.GetUint64())
case Float32Kind:
return (lv.GetFloat32() <= rv.GetFloat32()) // XXX determinism?
cmp, nan := softfloat.Fcmp64(softfloat.Float64(lv.GetFloat32()), softfloat.Float64(rv.GetFloat32()))
return (cmp <= 0) && !nan
case Float64Kind:
return (lv.GetFloat64() <= rv.GetFloat64()) // XXX determinism?
cmp, nan := softfloat.Fcmp64(lv.GetFloat64(), rv.GetFloat64())
return (cmp <= 0) && !nan
case BigintKind:
lb := lv.V.(BigintValue).V
rb := rv.V.(BigintValue).V
Expand Down Expand Up @@ -619,9 +626,11 @@ func isGtr(lv, rv *TypedValue) bool {
case Uint64Kind:
return (lv.GetUint64() > rv.GetUint64())
case Float32Kind:
return (lv.GetFloat32() > rv.GetFloat32()) // XXX determinism?
cmp, nan := softfloat.Fcmp64(softfloat.Float64(lv.GetFloat32()), softfloat.Float64(rv.GetFloat32()))
return (cmp > 0) && !nan
case Float64Kind:
return (lv.GetFloat64() > rv.GetFloat64()) // XXX determinism?
cmp, nan := softfloat.Fcmp64(lv.GetFloat64(), rv.GetFloat64())
return (cmp > 0) && !nan
case BigintKind:
lb := lv.V.(BigintValue).V
rb := rv.V.(BigintValue).V
Expand Down Expand Up @@ -663,9 +672,11 @@ func isGeq(lv, rv *TypedValue) bool {
case Uint64Kind:
return (lv.GetUint64() >= rv.GetUint64())
case Float32Kind:
return (lv.GetFloat32() >= rv.GetFloat32()) // XXX determinism?
cmp, nan := softfloat.Fcmp64(softfloat.Float64(lv.GetFloat32()), softfloat.Float64(rv.GetFloat32()))
return (cmp >= 0) && !nan
case Float64Kind:
return (lv.GetFloat64() >= rv.GetFloat64()) // XXX determinism?
cmp, nan := softfloat.Fcmp64(lv.GetFloat64(), rv.GetFloat64())
return (cmp >= 0) && !nan
case BigintKind:
lb := lv.V.(BigintValue).V
rb := rv.V.(BigintValue).V
Expand Down Expand Up @@ -713,10 +724,10 @@ func addAssign(alloc *Allocator, lv, rv *TypedValue) {
lv.SetUint64(lv.GetUint64() + rv.GetUint64())
case Float32Type:
// NOTE: gno doesn't fuse *+.
lv.SetFloat32(lv.GetFloat32() + rv.GetFloat32()) // XXX determinism?
lv.SetFloat32(softfloat.Fadd32(lv.GetFloat32(), rv.GetFloat32()))
case Float64Type:
// NOTE: gno doesn't fuse *+.
lv.SetFloat64(lv.GetFloat64() + rv.GetFloat64()) // XXX determinism?
lv.SetFloat64(softfloat.Fadd64(lv.GetFloat64(), rv.GetFloat64()))
case BigintType, UntypedBigintType:
lb := lv.GetBigInt()
lb = big.NewInt(0).Add(lb, rv.GetBigInt())
Expand Down Expand Up @@ -769,10 +780,10 @@ func subAssign(lv, rv *TypedValue) {
lv.SetUint64(lv.GetUint64() - rv.GetUint64())
case Float32Type:
// NOTE: gno doesn't fuse *+.
lv.SetFloat32(lv.GetFloat32() - rv.GetFloat32()) // XXX determinism?
lv.SetFloat32(softfloat.Fsub32(lv.GetFloat32(), rv.GetFloat32()))
case Float64Type:
// NOTE: gno doesn't fuse *+.
lv.SetFloat64(lv.GetFloat64() - rv.GetFloat64()) // XXX determinism?
lv.SetFloat64(softfloat.Fsub64(lv.GetFloat64(), rv.GetFloat64()))
case BigintType, UntypedBigintType:
lb := lv.GetBigInt()
lb = big.NewInt(0).Sub(lb, rv.GetBigInt())
Expand Down Expand Up @@ -825,10 +836,10 @@ func mulAssign(lv, rv *TypedValue) {
lv.SetUint64(lv.GetUint64() * rv.GetUint64())
case Float32Type:
// NOTE: gno doesn't fuse *+.
lv.SetFloat32(lv.GetFloat32() * rv.GetFloat32()) // XXX determinism?
lv.SetFloat32(softfloat.Fmul32(lv.GetFloat32(), rv.GetFloat32()))
case Float64Type:
// NOTE: gno doesn't fuse *+.
lv.SetFloat64(lv.GetFloat64() * rv.GetFloat64()) // XXX determinism?
lv.SetFloat64(softfloat.Fmul64(lv.GetFloat64(), rv.GetFloat64()))
case BigintType, UntypedBigintType:
lb := lv.GetBigInt()
lb = big.NewInt(0).Mul(lb, rv.GetBigInt())
Expand Down Expand Up @@ -916,18 +927,16 @@ func quoAssign(lv, rv *TypedValue) *Exception {
lv.SetUint64(lv.GetUint64() / rv.GetUint64())
case Float32Type:
// NOTE: gno doesn't fuse *+.
if rv.GetFloat32() == 0 {
if cmp, nan := softfloat.Fcmp64(softfloat.F32to64(rv.GetFloat32()), softfloat.Fint32to64(0)); cmp == 0 && !nan {
return expt
}
lv.SetFloat32(lv.GetFloat32() / rv.GetFloat32())
// XXX FOR DETERMINISM, PANIC IF NAN.
lv.SetFloat32(softfloat.Fdiv32(lv.GetFloat32(), rv.GetFloat32()))
case Float64Type:
// NOTE: gno doesn't fuse *+.
if rv.GetFloat64() == 0 {
if cmp, nan := softfloat.Fcmp64(rv.GetFloat64(), 0); cmp == 0 && !nan {
return expt
}
lv.SetFloat64(lv.GetFloat64() / rv.GetFloat64())
// XXX FOR DETERMINISM, PANIC IF NAN.
lv.SetFloat64(softfloat.Fdiv64(lv.GetFloat64(), rv.GetFloat64()))
case BigintType, UntypedBigintType:
if rv.GetBigInt().Sign() == 0 {
return expt
Expand Down
Loading

0 comments on commit 4fabee4

Please sign in to comment.