Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: softfloat support for GOARCH=wasm #62470

Open
qizhou opened this issue Sep 6, 2023 · 28 comments
Open

proposal: softfloat support for GOARCH=wasm #62470

qizhou opened this issue Sep 6, 2023 · 28 comments
Labels
Milestone

Comments

@qizhou
Copy link

qizhou commented Sep 6, 2023

We are working on proving the execution of go-compiled WASM code using zero-knowledge (ZK) (see https://github.com/DelphinusLab/zkWasm). However, the current zkWASM prover cannot support floating point instructions (f32.xxx/f64.xxx). We tried to use go build -gcflags=all=-d=softfloat and we found most of the FP instructions are translated to INT ones except f64.load, f64.store, f64.ceil, f64.floor.

Q: Is there a way to support softfloat explicitly for WASM or any flags to fully support softfloat?

Test code (fp.go):

import "math"

func main() {
	a := 3.5
	b := 2.7
	println(math.Ceil(a))
	println(math.Floor(a))
	println(a / b)
}

Commands

$ go version
go version go1.21.0 linux/amd64
$ GOOS=js GOARCH=wasm go build -gcflags=all=-d=softfloat fp.go
$ wasm2wat fp | grep 'f32\.'
$ wasm2wat fp | grep 'f64\.'
    f64.load offset=8
    f64.floor
    f64.store offset=16
    f64.load offset=8
    f64.ceil
    f64.store offset=16
@qizhou qizhou added the Proposal label Sep 6, 2023
@gopherbot gopherbot added this to the Proposal milestone Sep 6, 2023
@ianlancetaylor
Copy link
Contributor

CC @golang/wasm

@ianlancetaylor
Copy link
Contributor

This would presumably need to be an option in the GOWASM environment variable.

@johanbrandhorst
Copy link
Member

Have we introduced a GOWASM variable or are you proposing a new variable?

@rsc
Copy link
Contributor

rsc commented Sep 6, 2023

It seems truly ironic for a WASM implementation not to support floating point.

@qizhou
Copy link
Author

qizhou commented Sep 6, 2023

It seems truly ironic for a WASM implementation not to support floating point.

ZK technology that supports floating point instructions is much harder than integer ones - especially since most ZK/blockchain applications use high-resolution integers (mostly 256-bit) to perform financial applications such as balance transfer, token swap, compound rate, etc.

@LiuJiazheng
Copy link

LiuJiazheng commented Sep 11, 2023

Hi there, I am working with @qizhou trying to deliver a provable WASM program. It would be greatly appreciated if we could be guided the pointers or links which implement f32/f64 as software float simulation (i.e. how the softfloat takes effect, it seems right now that the support is partially), thus we might do some handcraft change ourselves : )

@LiuJiazheng
Copy link

Plus can confirm f32 is gone after using -d=softfloat
fp.go

func main() {
        a := float32(3.5)
        b := float32(2.7)
        println(a / b)
}

commands

% env GO111MODULE=on GOOS=wasip1 GOARCH=wasm go build -gcflags=all=-d=softfloat fp.go
% wasmtime fp
+1.296296e+000
% wasm2wat fp | grep 'f32\.' | wc -l
       0
% wasm2wat fp | grep 'f64\.' | wc -l
       0

@0x1cc
Copy link

0x1cc commented Sep 11, 2023

I think this is a problem caused by the incompatibility of the math library with the softfloat compilation option. Take math.Floor in (src/math/floor.go) as an example.

// src/math/floor.go Line 14

func Floor(x float64) float64 {
	if haveArchFloor {
		return archFloor(x)
	}
	return floor(x)
}

When the compilation architecture is WASM , it will directly uses the corresponding underlying instructions of WASM (src/math/floor_wasm.s). Therefore, softfloat does not work!

// src/math/floor_asm.go

//go:build 386 || amd64 || arm64 || ppc64 || ppc64le || s390x || wasm

package math

const haveArchFloor = true

@LiuJiazheng
Copy link

Okay now it looks more like a bug to me.

Is there a way to pass the gcflag into math package in runtime? Or a build flag could help this.

@randall77
Copy link
Contributor

-gcflags=all=-d=softfloat only affects the compiler (gc = Go Compiler). It does not affect any assembly code in the math package or elsewhere. Thus it isn't the equivalent of a GOWASM=softfloat build option.

@LiuJiazheng
Copy link

@randall77 Thanks. That makes sense.
I agree the proposal of adding GOWASM=softfloat would be an elegant way to implement that.

func gowasm() (f gowasmFeatures) {

@johanbrandhorst
Copy link
Member

Is this something we need a build option for, or could it just be the default behavior?

@LiuJiazheng
Copy link

Based on what I learned from Qi's proposal and conversation, my understanding is to, add a new build option.
Default behavior should (consistently) be using hardware floating point arithmetic for sake of performance, plus all modern browsers supporting it. This pr empowers users who are operating on weird platform(for example, zkWASM) which may be hard handling floating point.

@qizhou
Copy link
Author

qizhou commented Sep 22, 2023

We have temporarily solved the issue by replacing f64.floor and f64.ceil using the following code (by @0x1cc ):

+// golang issue: https://github.com/golang/go/issues/62470
+
+package math
+
+import "math"
+
+// Borrow from `src/math/floor.go`
+func Floor(x float64) float64 {
+       if x == 0 || math.IsNaN(x) || math.IsInf(x, 0) {
+               return x
+       }
+       if x < 0 {
+               d, fract := math.Modf(-x)
+               if fract != 0.0 {
+                       d = d + 1
+               }
+               return -d
+       }
+       d, _ := math.Modf(x)
+       return d
+}
+
+func Ceil(x float64) float64 {
+       return -Floor(-x)
+}

@johanbrandhorst
Copy link
Member

Should this be something we add a wasm arch build tagged file for in the math package?

@0x1cc
Copy link

0x1cc commented Sep 23, 2023

Not only for WASM, indeed, math package does not support softfloat well for all platform.

In the example (fp.go) provided by Qi, even GO386=softfloat does not work in the math package. This looks like a bug.

$ GOOS=linux GOARCH=386 GO386=softfloat go build fp.go 
$ go tool objdump -S fp | grep FRNDINT                 
  0x80a56be             d9fc                    FRNDINT
  0x80a56ee             d9fc                    FRNDINT

FRNDINT (Round to Integer) requires hardware support FPU.

If we want to support softfloat for all platform (even for architectures that in general would have FPU), we need to modify the math package. In the current math package, when the architecture has an FPU, it will by default accelerate the computation using assembling code that takes advantage of the architecture's FPU. But this will lead to incompatibility with softfloat. As shown in the example above, GO386=softfloat does not work in the math package.

@randall77
Copy link
Contributor

@0x1cc : that is not a bug. FRNDINT is used by the 387 coprocessor to implement floating point operations. It is not an SSE instruction.
We assume that 386 chips have at least Pentium MMX or better. See https://github.com/golang/go/wiki/MinimumRequirements#386 . That includes MMX instructions like FRNDINT but not SSE instructions.

@randall77
Copy link
Contributor

But in any case, that's a separate issue than this one. @0x1cc Please open an new issue if you disagree with how softfloat is implemented on 386 (this issue is about wasm).

@0x1cc
Copy link

0x1cc commented Sep 23, 2023

@randall77 Thanks! Regarding the softfloat support for WASM, using -gcflags=all=-d=softfloat can convert floating-point calculations to softfloat within the compiler, but it doesn't impact any assembly code in the math package. To incorporate softfloat support for WASM, or possibly other architectures, should we consider introducing a new build option that disables the math package from utilizing FPU-related assembly code for performance optimization?

The math package can leverage assembly code for performance optimization by default. However, when we require softfloat support, we can utilize an option to deactivate this feature. This option will enable softfloat support with -gcflags=all=-d=softfloat for WASM and potentially all platforms.

@qizhou

This comment was marked as off-topic.

@johanbrandhorst

This comment was marked as resolved.

@0x1cc
Copy link

0x1cc commented Sep 28, 2023

To support softfloat in the Go math package, we propose adding a new build tag, let's call it math_pure_go, that users can apply to disable the use of FPU-specific assembly code in the math package. We can use the math_pure_go build tag to disable the assembly in math package. With the build tag -tags=math_pure_go and -gcflags=all=-d=softfloat, we can support softfloat for all platforms.

go build -gcflags=all=-d=softfloat -tags=math_pure_go

For additional details, please refer to the corresponding issue: GitHub Issue #63270.

@johanbrandhorst
Copy link
Member

Can we close this issue in favor of the new proposal?

@bcmills
Copy link
Contributor

bcmills commented Sep 28, 2023

the current zkWASM prover cannot support floating point instructions (f32.xxx/f64.xxx)

Can you give more detail as to why you would prefer to add another Go platform variant rather than implement support in zkWASM for the necessary floating-point instructions?

It seems like either way you'd be emulating floating-point arithmetic using a framework designed for integers.

@ianlancetaylor
Copy link
Contributor

@johanbrandhorst Using a GOWASM=softfloat variable seems more consistent with how other targets behave than the approach described in #63270. I don't think we should close this proposal yet.

Like others, I also don't understand how many people need this feature. Is there enough need here that this is a platform that we ought to support?

@LiuJiazheng
Copy link

There are two separate questions, as I could think up:
First, is there necessary to support softfloat on golang? This is subtle. As many reply states modern browser and other archs should have hardware support for fp. But there is always exception ---- as Qi points out, if you want to do calculation on an elliptic curve group using a generic programming language, it requires to set up a large prime field (see a rust implementation here https://github.com/zcash/pasta_curves). It is generally dealing with a large int, you might imagine, but not any float number. For such applications, it is hard for developers to think about fp support because they do not really need it. Again, I DO NOT think this MUST BE DONE in upstream, which is go community. But I do want to demonstrate the (maybe niche) demand from users.
Second, without any background, when I use -gcflags=all=-d=softfloat I naively believe this could help me eliminate all fp in target asm. This is quite confusing that the code written by customers could be fp-free but the standard library like math still holds quite an amount of them. Personally I don't think it is intuitive. And this inconsistency concerns users (at least me).
Again, I hold same question about "should golang support softfloat" but I strongly believe that the 'inconsistency' (forgive me if you have a better wording) must be solved -- at least a note stating that, so that people with same demand might not encounter the situation again, hopefully.

@ianlancetaylor
Copy link
Contributor

Note that -gcflags=all=-d=softfloat isn't documented at all. We make no promise that we will retain that feature.

The closest thing I can find to documentation is from go tool compile -d help which says, for softfloat: force compiler to emit soft-float code. It doesn't say anything about assembly code. It may not be intuitive but I don't think that very short comment about an undocumented feature needs to say anything about assembly code.

Where did you see a mention of -d=softfloat? Perhaps we could mention assembly code there.

@qizhou
Copy link
Author

qizhou commented Sep 29, 2023

I find -d=softfloat in #61588 (comment), which faces the same issue for GOARM=6, 7.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Incoming
Development

No branches or pull requests

9 participants