From 8d810286b3121b601480426159c04d178fa29166 Mon Sep 17 00:00:00 2001 From: David Chase Date: Wed, 1 Oct 2025 11:29:13 -0400 Subject: [PATCH] cmd/compile: make wasm match other platforms for FP->int32/64 conversions this change is for overflows, so that all the platforms agree. Change-Id: I9f459353615bf24ef8a5de641063d9ce34986241 Reviewed-on: https://go-review.googlesource.com/c/go/+/708358 LUCI-TryBot-Result: Go LUCI Reviewed-by: Keith Randall Reviewed-by: Keith Randall --- src/cmd/compile/internal/ssa/_gen/Wasm.rules | 9 ++-- src/cmd/compile/internal/ssa/_gen/WasmOps.go | 17 ++++-- src/cmd/compile/internal/ssa/opGen.go | 56 ++++++++++++++++++++ src/cmd/compile/internal/ssa/rewriteWasm.go | 8 +-- src/cmd/compile/internal/wasm/ssa.go | 11 ++++ test/convert5.go | 5 -- 6 files changed, 89 insertions(+), 17 deletions(-) diff --git a/src/cmd/compile/internal/ssa/_gen/Wasm.rules b/src/cmd/compile/internal/ssa/_gen/Wasm.rules index f632a01109..bb123bc3cc 100644 --- a/src/cmd/compile/internal/ssa/_gen/Wasm.rules +++ b/src/cmd/compile/internal/ssa/_gen/Wasm.rules @@ -76,13 +76,14 @@ (Cvt32Uto(64|32)F x) => (F(64|32)ConvertI64U (ZeroExt32to64 x)) (Cvt64Uto(64|32)F ...) => (F(64|32)ConvertI64U ...) -(Cvt32Fto32 ...) => (I64TruncSatF32S ...) +(Cvt32Fto32 ...) => (I32TruncSatF32S ...) (Cvt32Fto64 ...) => (I64TruncSatF32S ...) -(Cvt64Fto32 ...) => (I64TruncSatF64S ...) +(Cvt64Fto32 ...) => (I32TruncSatF64S ...) (Cvt64Fto64 ...) => (I64TruncSatF64S ...) -(Cvt32Fto32U ...) => (I64TruncSatF32U ...) + +(Cvt32Fto32U ...) => (I32TruncSatF32U ...) (Cvt32Fto64U ...) => (I64TruncSatF32U ...) -(Cvt64Fto32U ...) => (I64TruncSatF64U ...) +(Cvt64Fto32U ...) => (I32TruncSatF64U ...) (Cvt64Fto64U ...) => (I64TruncSatF64U ...) (Cvt32Fto64F ...) => (F64PromoteF32 ...) diff --git a/src/cmd/compile/internal/ssa/_gen/WasmOps.go b/src/cmd/compile/internal/ssa/_gen/WasmOps.go index 45bbed5f52..b63f28a219 100644 --- a/src/cmd/compile/internal/ssa/_gen/WasmOps.go +++ b/src/cmd/compile/internal/ssa/_gen/WasmOps.go @@ -222,10 +222,19 @@ func init() { {name: "F64Mul", asm: "F64Mul", argLength: 2, reg: fp64_21, typ: "Float64"}, // arg0 * arg1 {name: "F64Div", asm: "F64Div", argLength: 2, reg: fp64_21, typ: "Float64"}, // arg0 / arg1 - {name: "I64TruncSatF64S", asm: "I64TruncSatF64S", argLength: 1, reg: regInfo{inputs: []regMask{fp64}, outputs: []regMask{gp}}, typ: "Int64"}, // truncates the float arg0 to a signed integer (saturating) - {name: "I64TruncSatF64U", asm: "I64TruncSatF64U", argLength: 1, reg: regInfo{inputs: []regMask{fp64}, outputs: []regMask{gp}}, typ: "Int64"}, // truncates the float arg0 to an unsigned integer (saturating) - {name: "I64TruncSatF32S", asm: "I64TruncSatF32S", argLength: 1, reg: regInfo{inputs: []regMask{fp32}, outputs: []regMask{gp}}, typ: "Int64"}, // truncates the float arg0 to a signed integer (saturating) - {name: "I64TruncSatF32U", asm: "I64TruncSatF32U", argLength: 1, reg: regInfo{inputs: []regMask{fp32}, outputs: []regMask{gp}}, typ: "Int64"}, // truncates the float arg0 to an unsigned integer (saturating) + {name: "I64TruncSatF64S", asm: "I64TruncSatF64S", argLength: 1, reg: regInfo{inputs: []regMask{fp64}, outputs: []regMask{gp}}, typ: "Int64"}, // truncates the float arg0 to a signed integer (saturating) + {name: "I64TruncSatF64U", asm: "I64TruncSatF64U", argLength: 1, reg: regInfo{inputs: []regMask{fp64}, outputs: []regMask{gp}}, typ: "Uint64"}, // truncates the float arg0 to an unsigned integer (saturating) + {name: "I64TruncSatF32S", asm: "I64TruncSatF32S", argLength: 1, reg: regInfo{inputs: []regMask{fp32}, outputs: []regMask{gp}}, typ: "Int64"}, // truncates the float arg0 to a signed integer (saturating) + {name: "I64TruncSatF32U", asm: "I64TruncSatF32U", argLength: 1, reg: regInfo{inputs: []regMask{fp32}, outputs: []regMask{gp}}, typ: "Uint64"}, // truncates the float arg0 to an unsigned integer (saturating) + + // It appears to be wasm convention that everything lands in a 64-bit register; + // the WASM instructions for these operations produce 32-bit width results, but + // wasm/ssa.go widens them appropriately to 64-bit results. + {name: "I32TruncSatF64S", asm: "I32TruncSatF64S", argLength: 1, reg: regInfo{inputs: []regMask{fp64}, outputs: []regMask{gp}}, typ: "Int64"}, // truncates the float arg0 to a signed integer (saturating) + {name: "I32TruncSatF64U", asm: "I32TruncSatF64U", argLength: 1, reg: regInfo{inputs: []regMask{fp64}, outputs: []regMask{gp}}, typ: "Uint64"}, // truncates the float arg0 to an unsigned integer (saturating) + {name: "I32TruncSatF32S", asm: "I32TruncSatF32S", argLength: 1, reg: regInfo{inputs: []regMask{fp32}, outputs: []regMask{gp}}, typ: "Int64"}, // truncates the float arg0 to a signed integer (saturating) + {name: "I32TruncSatF32U", asm: "I32TruncSatF32U", argLength: 1, reg: regInfo{inputs: []regMask{fp32}, outputs: []regMask{gp}}, typ: "Uint64"}, // truncates the float arg0 to an unsigned integer (saturating) + {name: "F32ConvertI64S", asm: "F32ConvertI64S", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{fp32}}, typ: "Float32"}, // converts the signed integer arg0 to a float {name: "F32ConvertI64U", asm: "F32ConvertI64U", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{fp32}}, typ: "Float32"}, // converts the unsigned integer arg0 to a float {name: "F64ConvertI64S", asm: "F64ConvertI64S", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{fp64}}, typ: "Float64"}, // converts the signed integer arg0 to a float diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 2a60398bc6..32ba3a8985 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -2980,6 +2980,10 @@ const ( OpWasmI64TruncSatF64U OpWasmI64TruncSatF32S OpWasmI64TruncSatF32U + OpWasmI32TruncSatF64S + OpWasmI32TruncSatF64U + OpWasmI32TruncSatF32S + OpWasmI32TruncSatF32U OpWasmF32ConvertI64S OpWasmF32ConvertI64U OpWasmF64ConvertI64S @@ -40296,6 +40300,58 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "I32TruncSatF64S", + argLen: 1, + asm: wasm.AI32TruncSatF64S, + reg: regInfo{ + inputs: []inputInfo{ + {0, 281470681743360}, // F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 65535}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 + }, + }, + }, + { + name: "I32TruncSatF64U", + argLen: 1, + asm: wasm.AI32TruncSatF64U, + reg: regInfo{ + inputs: []inputInfo{ + {0, 281470681743360}, // F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 65535}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 + }, + }, + }, + { + name: "I32TruncSatF32S", + argLen: 1, + asm: wasm.AI32TruncSatF32S, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + outputs: []outputInfo{ + {0, 65535}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 + }, + }, + }, + { + name: "I32TruncSatF32U", + argLen: 1, + asm: wasm.AI32TruncSatF32U, + reg: regInfo{ + inputs: []inputInfo{ + {0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + outputs: []outputInfo{ + {0, 65535}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 + }, + }, + }, { name: "F32ConvertI64S", argLen: 1, diff --git a/src/cmd/compile/internal/ssa/rewriteWasm.go b/src/cmd/compile/internal/ssa/rewriteWasm.go index a164a6eee5..dd73d3a5e7 100644 --- a/src/cmd/compile/internal/ssa/rewriteWasm.go +++ b/src/cmd/compile/internal/ssa/rewriteWasm.go @@ -120,10 +120,10 @@ func rewriteValueWasm(v *Value) bool { v.Op = OpWasmI64Ctz return true case OpCvt32Fto32: - v.Op = OpWasmI64TruncSatF32S + v.Op = OpWasmI32TruncSatF32S return true case OpCvt32Fto32U: - v.Op = OpWasmI64TruncSatF32U + v.Op = OpWasmI32TruncSatF32U return true case OpCvt32Fto64: v.Op = OpWasmI64TruncSatF32S @@ -143,13 +143,13 @@ func rewriteValueWasm(v *Value) bool { case OpCvt32to64F: return rewriteValueWasm_OpCvt32to64F(v) case OpCvt64Fto32: - v.Op = OpWasmI64TruncSatF64S + v.Op = OpWasmI32TruncSatF64S return true case OpCvt64Fto32F: v.Op = OpWasmF32DemoteF64 return true case OpCvt64Fto32U: - v.Op = OpWasmI64TruncSatF64U + v.Op = OpWasmI32TruncSatF64U return true case OpCvt64Fto64: v.Op = OpWasmI64TruncSatF64S diff --git a/src/cmd/compile/internal/wasm/ssa.go b/src/cmd/compile/internal/wasm/ssa.go index 1e3b318e8c..8ebc902885 100644 --- a/src/cmd/compile/internal/wasm/ssa.go +++ b/src/cmd/compile/internal/wasm/ssa.go @@ -430,6 +430,17 @@ func ssaGenValueOnStack(s *ssagen.State, v *ssa.Value, extend bool) { getValue64(s, v.Args[0]) s.Prog(v.Op.Asm()) + // 32-bit integer conversion results + case ssa.OpWasmI32TruncSatF32S, ssa.OpWasmI32TruncSatF64S: + getValue64(s, v.Args[0]) + s.Prog(v.Op.Asm()) + s.Prog(wasm.AI64ExtendI32S) + + case ssa.OpWasmI32TruncSatF32U, ssa.OpWasmI32TruncSatF64U: + getValue64(s, v.Args[0]) + s.Prog(v.Op.Asm()) + s.Prog(wasm.AI64ExtendI32U) + case ssa.OpWasmF32DemoteF64: getValue64(s, v.Args[0]) s.Prog(v.Op.Asm()) diff --git a/test/convert5.go b/test/convert5.go index 27aa7867f4..4688fae855 100644 --- a/test/convert5.go +++ b/test/convert5.go @@ -4,11 +4,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !wasm - -// TODO fix this to work for wasm -// Doing more than this, however, expands the change. - package main import ( -- 2.52.0