]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: add saturating conversions on wasm
authorRichard Musiol <mail@richard-musiol.de>
Sun, 24 Mar 2019 11:14:27 +0000 (12:14 +0100)
committerRichard Musiol <neelance@gmail.com>
Thu, 4 Apr 2019 16:10:12 +0000 (16:10 +0000)
This change adds the GOWASM option "satconv" to enable the generation
of experimental saturating (non-trapping) float-to-int conversions.
It improves the performance of the conversion by 42%.

Previously the conversions had already been augmented with helper
functions to have saturating behavior. Now Wasm.rules is always using
the new operation names and wasm/ssa.go is falling back to the helpers
if the feature is not enabled.

The feature is in phase 4 of the WebAssembly proposal process:
https://github.com/WebAssembly/meetings/blob/master/process/phases.md

More information on the feature can be found at:
https://github.com/WebAssembly/nontrapping-float-to-int-conversions/blob/master/proposals/nontrapping-float-to-int-conversion/Overview.md

Change-Id: Ic6c3688017054ede804b02b6b0ffd4a02ef33ad7
Reviewed-on: https://go-review.googlesource.com/c/go/+/170119
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>

12 files changed:
doc/install-source.html
src/cmd/compile/internal/ssa/gen/Wasm.rules
src/cmd/compile/internal/ssa/gen/WasmOps.go
src/cmd/compile/internal/ssa/opGen.go
src/cmd/compile/internal/ssa/rewriteWasm.go
src/cmd/compile/internal/wasm/ssa.go
src/cmd/go/alldocs.go
src/cmd/go/internal/help/helpdoc.go
src/cmd/internal/obj/wasm/a.out.go
src/cmd/internal/obj/wasm/anames.go
src/cmd/internal/obj/wasm/wasmobj.go
src/cmd/internal/objabi/util.go

index 9c73b925b1b3e28a770f1403b6587a0e92bdbd4a..6a0c3844ae9501794cfd84a9fbb0022fb9182f12 100644 (file)
@@ -645,6 +645,7 @@ for which the compiler will target. The default is <code>power8</code>.
        The default is to use no experimental features.
        </p>
        <ul>
+               <li><code>GOWASM=satconv</code>: generate <a href="https://github.com/WebAssembly/nontrapping-float-to-int-conversions/blob/master/proposals/nontrapping-float-to-int-conversion/Overview.md">saturating (non-trapping) float-to-int conversions</a></li>
                <li><code>GOWASM=signext</code>: generate <a href="https://github.com/WebAssembly/sign-extension-ops/blob/master/proposals/sign-extension-ops/Overview.md">sign-extension operators</a></li>
        </ul>
 </li>
index 72f4805edf213dc54dbebc34777372d720bf75af..a832abf0fb4f469f52125eb5fbaa454b229c205b 100644 (file)
 (Cvt64Uto32F x) -> (LoweredRound32F (F64ConvertI64U x))
 (Cvt64Uto64F x) -> (F64ConvertI64U x)
 
-(Cvt32Fto32 x) -> (I64TruncF64S x)
-(Cvt32Fto64 x) -> (I64TruncF64S x)
-(Cvt64Fto32 x) -> (I64TruncF64S x)
-(Cvt64Fto64 x) -> (I64TruncF64S x)
-(Cvt32Fto32U x) -> (I64TruncF64U x)
-(Cvt32Fto64U x) -> (I64TruncF64U x)
-(Cvt64Fto32U x) -> (I64TruncF64U x)
-(Cvt64Fto64U x) -> (I64TruncF64U x)
+(Cvt32Fto32 x) -> (I64TruncSatF64S x)
+(Cvt32Fto64 x) -> (I64TruncSatF64S x)
+(Cvt64Fto32 x) -> (I64TruncSatF64S x)
+(Cvt64Fto64 x) -> (I64TruncSatF64S x)
+(Cvt32Fto32U x) -> (I64TruncSatF64U x)
+(Cvt32Fto64U x) -> (I64TruncSatF64U x)
+(Cvt64Fto32U x) -> (I64TruncSatF64U x)
+(Cvt64Fto64U x) -> (I64TruncSatF64U x)
 
 (Cvt32Fto64F x) -> x
 (Cvt64Fto32F x) -> (LoweredRound32F x)
index 4e5f0765750d6078e032c31cd3d4f669111fc567..de035c985addf95505f20c2fc2353581fe1d575a 100644 (file)
@@ -187,8 +187,8 @@ func init() {
                {name: "F64Mul", asm: "F64Mul", argLength: 2, reg: fp21, typ: "Float64"}, // arg0 * arg1
                {name: "F64Div", asm: "F64Div", argLength: 2, reg: fp21, typ: "Float64"}, // arg0 / arg1
 
-               {name: "I64TruncF64S", asm: "I64TruncF64S", argLength: 1, reg: regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}, typ: "Int64"},       // truncates the float arg0 to a signed integer
-               {name: "I64TruncF64U", asm: "I64TruncF64U", argLength: 1, reg: regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}, typ: "Int64"},       // truncates the float arg0 to an unsigned integer
+               {name: "I64TruncSatF64S", asm: "I64TruncSatF64S", argLength: 1, reg: regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}, typ: "Int64"}, // truncates the float arg0 to a signed integer (saturating)
+               {name: "I64TruncSatF64U", asm: "I64TruncSatF64U", argLength: 1, reg: regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}, typ: "Int64"}, // truncates the float arg0 to an unsigned integer (saturating)
                {name: "F64ConvertI64S", asm: "F64ConvertI64S", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}, typ: "Float64"}, // converts the signed integer arg0 to a float
                {name: "F64ConvertI64U", asm: "F64ConvertI64U", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}, typ: "Float64"}, // converts the unsigned integer arg0 to a float
 
index 214d68757c447ed898a652ddd4ad343977291149..06dcb2d7ac5573b63f0ac7b152023e183a566aca 100644 (file)
@@ -2132,8 +2132,8 @@ const (
        OpWasmF64Sub
        OpWasmF64Mul
        OpWasmF64Div
-       OpWasmI64TruncF64S
-       OpWasmI64TruncF64U
+       OpWasmI64TruncSatF64S
+       OpWasmI64TruncSatF64U
        OpWasmF64ConvertI64S
        OpWasmF64ConvertI64U
        OpWasmI64Extend8S
@@ -28676,9 +28676,9 @@ var opcodeTable = [...]opInfo{
                },
        },
        {
-               name:   "I64TruncF64S",
+               name:   "I64TruncSatF64S",
                argLen: 1,
-               asm:    wasm.AI64TruncF64S,
+               asm:    wasm.AI64TruncSatF64S,
                reg: regInfo{
                        inputs: []inputInfo{
                                {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
@@ -28689,9 +28689,9 @@ var opcodeTable = [...]opInfo{
                },
        },
        {
-               name:   "I64TruncF64U",
+               name:   "I64TruncSatF64U",
                argLen: 1,
-               asm:    wasm.AI64TruncF64U,
+               asm:    wasm.AI64TruncSatF64U,
                reg: regInfo{
                        inputs: []inputInfo{
                                {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
index fe85922e31b06128689d10351679b32f4a25d7e7..d02ed1e87f916ac0f9dd4d2ffa87839d0885989b 100644 (file)
@@ -1095,10 +1095,10 @@ func rewriteValueWasm_OpCtz8NonZero_0(v *Value) bool {
 func rewriteValueWasm_OpCvt32Fto32_0(v *Value) bool {
        // match: (Cvt32Fto32 x)
        // cond:
-       // result: (I64TruncF64S x)
+       // result: (I64TruncSatF64S x)
        for {
                x := v.Args[0]
-               v.reset(OpWasmI64TruncF64S)
+               v.reset(OpWasmI64TruncSatF64S)
                v.AddArg(x)
                return true
        }
@@ -1106,10 +1106,10 @@ func rewriteValueWasm_OpCvt32Fto32_0(v *Value) bool {
 func rewriteValueWasm_OpCvt32Fto32U_0(v *Value) bool {
        // match: (Cvt32Fto32U x)
        // cond:
-       // result: (I64TruncF64U x)
+       // result: (I64TruncSatF64U x)
        for {
                x := v.Args[0]
-               v.reset(OpWasmI64TruncF64U)
+               v.reset(OpWasmI64TruncSatF64U)
                v.AddArg(x)
                return true
        }
@@ -1117,10 +1117,10 @@ func rewriteValueWasm_OpCvt32Fto32U_0(v *Value) bool {
 func rewriteValueWasm_OpCvt32Fto64_0(v *Value) bool {
        // match: (Cvt32Fto64 x)
        // cond:
-       // result: (I64TruncF64S x)
+       // result: (I64TruncSatF64S x)
        for {
                x := v.Args[0]
-               v.reset(OpWasmI64TruncF64S)
+               v.reset(OpWasmI64TruncSatF64S)
                v.AddArg(x)
                return true
        }
@@ -1140,10 +1140,10 @@ func rewriteValueWasm_OpCvt32Fto64F_0(v *Value) bool {
 func rewriteValueWasm_OpCvt32Fto64U_0(v *Value) bool {
        // match: (Cvt32Fto64U x)
        // cond:
-       // result: (I64TruncF64U x)
+       // result: (I64TruncSatF64U x)
        for {
                x := v.Args[0]
-               v.reset(OpWasmI64TruncF64U)
+               v.reset(OpWasmI64TruncSatF64U)
                v.AddArg(x)
                return true
        }
@@ -1215,10 +1215,10 @@ func rewriteValueWasm_OpCvt32to64F_0(v *Value) bool {
 func rewriteValueWasm_OpCvt64Fto32_0(v *Value) bool {
        // match: (Cvt64Fto32 x)
        // cond:
-       // result: (I64TruncF64S x)
+       // result: (I64TruncSatF64S x)
        for {
                x := v.Args[0]
-               v.reset(OpWasmI64TruncF64S)
+               v.reset(OpWasmI64TruncSatF64S)
                v.AddArg(x)
                return true
        }
@@ -1237,10 +1237,10 @@ func rewriteValueWasm_OpCvt64Fto32F_0(v *Value) bool {
 func rewriteValueWasm_OpCvt64Fto32U_0(v *Value) bool {
        // match: (Cvt64Fto32U x)
        // cond:
-       // result: (I64TruncF64U x)
+       // result: (I64TruncSatF64U x)
        for {
                x := v.Args[0]
-               v.reset(OpWasmI64TruncF64U)
+               v.reset(OpWasmI64TruncSatF64U)
                v.AddArg(x)
                return true
        }
@@ -1248,10 +1248,10 @@ func rewriteValueWasm_OpCvt64Fto32U_0(v *Value) bool {
 func rewriteValueWasm_OpCvt64Fto64_0(v *Value) bool {
        // match: (Cvt64Fto64 x)
        // cond:
-       // result: (I64TruncF64S x)
+       // result: (I64TruncSatF64S x)
        for {
                x := v.Args[0]
-               v.reset(OpWasmI64TruncF64S)
+               v.reset(OpWasmI64TruncSatF64S)
                v.AddArg(x)
                return true
        }
@@ -1259,10 +1259,10 @@ func rewriteValueWasm_OpCvt64Fto64_0(v *Value) bool {
 func rewriteValueWasm_OpCvt64Fto64U_0(v *Value) bool {
        // match: (Cvt64Fto64U x)
        // cond:
-       // result: (I64TruncF64U x)
+       // result: (I64TruncSatF64U x)
        for {
                x := v.Args[0]
-               v.reset(OpWasmI64TruncF64U)
+               v.reset(OpWasmI64TruncSatF64U)
                v.AddArg(x)
                return true
        }
index 7575df548aecee55cdd8165e8e519d21b012a76f..63eb319edb8d3307eb6b17e16073c165bafcfe29 100644 (file)
@@ -10,6 +10,7 @@ import (
        "cmd/compile/internal/types"
        "cmd/internal/obj"
        "cmd/internal/obj/wasm"
+       "cmd/internal/objabi"
 )
 
 func Init(arch *gc.Arch) {
@@ -307,15 +308,23 @@ func ssaGenValueOnStack(s *gc.SSAGenState, v *ssa.Value) {
                }
                s.Prog(wasm.AI64DivS)
 
-       case ssa.OpWasmI64TruncF64S:
+       case ssa.OpWasmI64TruncSatF64S:
                getValue64(s, v.Args[0])
-               p := s.Prog(wasm.ACall)
-               p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: gc.WasmTruncS}
+               if objabi.GOWASM.SatConv {
+                       s.Prog(v.Op.Asm())
+               } else {
+                       p := s.Prog(wasm.ACall)
+                       p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: gc.WasmTruncS}
+               }
 
-       case ssa.OpWasmI64TruncF64U:
+       case ssa.OpWasmI64TruncSatF64U:
                getValue64(s, v.Args[0])
-               p := s.Prog(wasm.ACall)
-               p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: gc.WasmTruncU}
+               if objabi.GOWASM.SatConv {
+                       s.Prog(v.Op.Asm())
+               } else {
+                       p := s.Prog(wasm.ACall)
+                       p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: gc.WasmTruncU}
+               }
 
        case
                ssa.OpWasmF64Neg, ssa.OpWasmF64ConvertI64S, ssa.OpWasmF64ConvertI64U,
index de07d910d8419443523a165b33535802e973b132..008e306efbdb30f3f4233c4a8456a7d817187383 100644 (file)
 //             Valid values are hardfloat (default), softfloat.
 //     GOWASM
 //             For GOARCH=wasm, comma-separated list of experimental WebAssembly features to use.
-//             Valid values are: signext.
+//             Valid values are satconv, signext.
 //
 // Special-purpose environment variables:
 //
index eb663e99b6739734f018fd80e44e728a938f2b48..98d4bd0382a042943ef0c431752905f416825fb8 100644 (file)
@@ -565,7 +565,7 @@ Architecture-specific environment variables:
                Valid values are hardfloat (default), softfloat.
        GOWASM
                For GOARCH=wasm, comma-separated list of experimental WebAssembly features to use.
-               Valid values are: signext.
+               Valid values are satconv, signext.
 
 Special-purpose environment variables:
 
index 29ea87f3b08806fc2fc0290c662a55851636ad8b..c686f1d6f0ec086a4a78ea24f2f509215128cf07 100644 (file)
@@ -216,6 +216,15 @@ const (
        AI64Extend16S
        AI64Extend32S
 
+       AI32TruncSatF32S // opcode 0xFC 0x00
+       AI32TruncSatF32U
+       AI32TruncSatF64S
+       AI32TruncSatF64U
+       AI64TruncSatF32S
+       AI64TruncSatF32U
+       AI64TruncSatF64S
+       AI64TruncSatF64U
+
        ALast // Sentinel: End of low-level WebAssembly instructions.
 
        ARESUMEPOINT
index fb4b72c39872c1936f8dc43c5e583456a8893e37..c8552e7f1832bcc0779e1d5a4d6d59eadad22d49 100644 (file)
@@ -182,6 +182,14 @@ var Anames = []string{
        "I64Extend8S",
        "I64Extend16S",
        "I64Extend32S",
+       "I32TruncSatF32S",
+       "I32TruncSatF32U",
+       "I32TruncSatF64S",
+       "I32TruncSatF64U",
+       "I64TruncSatF32S",
+       "I64TruncSatF32U",
+       "I64TruncSatF64S",
+       "I64TruncSatF64U",
        "Last",
        "RESUMEPOINT",
        "CALLNORESUME",
index dded62a4be24b3c3580fb1d9c70a59da2bcb2f31..0474e3b4b1266b39ec8088be66d8c0cafcc40d12 100644 (file)
@@ -886,7 +886,7 @@ func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
                }
 
                switch {
-               case p.As < AUnreachable || p.As >= ALast:
+               case p.As < AUnreachable:
                        panic(fmt.Sprintf("unexpected assembler op: %s", p.As))
                case p.As < AEnd:
                        w.WriteByte(byte(p.As - AUnreachable + 0x00))
@@ -894,8 +894,13 @@ func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
                        w.WriteByte(byte(p.As - AEnd + 0x0B))
                case p.As < AI32Load:
                        w.WriteByte(byte(p.As - ADrop + 0x1A))
-               default:
+               case p.As < AI32TruncSatF32S:
                        w.WriteByte(byte(p.As - AI32Load + 0x28))
+               case p.As < ALast:
+                       w.WriteByte(0xFC)
+                       w.WriteByte(byte(p.As - AI32TruncSatF32S + 0x00))
+               default:
+                       panic(fmt.Sprintf("unexpected assembler op: %s", p.As))
                }
 
                switch p.As {
index 02f9d9273ae677ef53d912ae80a1fd74893269e3..e28447d14199c38145e52b3c640c32cd00a5880c 100644 (file)
@@ -79,10 +79,14 @@ func goppc64() int {
 
 type gowasmFeatures struct {
        SignExt bool
+       SatConv bool
 }
 
 func (f *gowasmFeatures) String() string {
        var flags []string
+       if f.SatConv {
+               flags = append(flags, "satconv")
+       }
        if f.SignExt {
                flags = append(flags, "signext")
        }
@@ -92,6 +96,8 @@ func (f *gowasmFeatures) String() string {
 func gowasm() (f gowasmFeatures) {
        for _, opt := range strings.Split(envOr("GOWASM", ""), ",") {
                switch opt {
+               case "satconv":
+                       f.SatConv = true
                case "signext":
                        f.SignExt = true
                case "":