From 55de3efde3a2e7789b866a66c81a1fcd7c374f09 Mon Sep 17 00:00:00 2001 From: George Adams Date: Wed, 18 Mar 2026 09:16:54 +0000 Subject: [PATCH] cmd/compile: elide sign-extend after zero-extend for wasm Add rules to eliminate sign-extension of values that have already been zero-extended from fewer bits via an I64And mask: (I64Extend32S x:(I64And _ (I64Const [c]))) && c >= 0 && int64(int32(c)) == c => x (I64Extend16S x:(I64And _ (I64Const [c]))) && c >= 0 && int64(int16(c)) == c => x (I64Extend8S x:(I64And _ (I64Const [c]))) && c >= 0 && int64(int8(c)) == c => x When a value has been masked to fit within the non-negative range of the sign-extension width, the upper bits are already zero and sign- extending is a no-op. For example, (I64Extend32S (I64And x 0xff)) can be elided because 0xff fits in a signed int32, so bit 31 is guaranteed to be zero and sign-extending from 32 bits is identity. Cq-Include-Trybots: luci.golang.try:gotip-wasip1-wasm_wasmtime,gotip-wasip1-wasm_wazero Change-Id: Ia54d67358756e47ca7635a6a8ca4beadb003820a Reviewed-on: https://go-review.googlesource.com/c/go/+/756320 Reviewed-by: Keith Randall Reviewed-by: Carlos Amedee LUCI-TryBot-Result: Go LUCI Reviewed-by: Keith Randall Auto-Submit: Keith Randall --- src/cmd/compile/internal/ssa/_gen/Wasm.rules | 7 +++ src/cmd/compile/internal/ssa/rewriteWasm.go | 60 ++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/src/cmd/compile/internal/ssa/_gen/Wasm.rules b/src/cmd/compile/internal/ssa/_gen/Wasm.rules index 3fef540658..31d37db657 100644 --- a/src/cmd/compile/internal/ssa/_gen/Wasm.rules +++ b/src/cmd/compile/internal/ssa/_gen/Wasm.rules @@ -412,6 +412,13 @@ (I64Extend32S (I64Extend8S x)) => (I64Extend8S x) (I64Extend16S (I64Extend8S x)) => (I64Extend8S x) +// Sign-extend of a value already zero-extended from fewer bits is a no-op. +// E.g. (I64Extend32S (I64And x (I64Const [0xff]))) — top 33 bits are already +// zero, so sign-extending from 32 is identity. +(I64Extend32S x:(I64And _ (I64Const [c]))) && c >= 0 && int64(int32(c)) == c => x +(I64Extend16S x:(I64And _ (I64Const [c]))) && c >= 0 && int64(int16(c)) == c => x +(I64Extend8S x:(I64And _ (I64Const [c]))) && c >= 0 && int64(int8(c)) == c => x + // TODO: declare these operations as commutative and get rid of these rules? (I64Add (I64Const [x]) y) && y.Op != OpWasmI64Const => (I64Add y (I64Const [x])) (I64Mul (I64Const [x]) y) && y.Op != OpWasmI64Const => (I64Mul y (I64Const [x])) diff --git a/src/cmd/compile/internal/ssa/rewriteWasm.go b/src/cmd/compile/internal/ssa/rewriteWasm.go index 80c7855a45..1c9f5073e3 100644 --- a/src/cmd/compile/internal/ssa/rewriteWasm.go +++ b/src/cmd/compile/internal/ssa/rewriteWasm.go @@ -4135,6 +4135,26 @@ func rewriteValueWasm_OpWasmI64Extend16S(v *Value) bool { v.AddArg(x) return true } + // match: (I64Extend16S x:(I64And _ (I64Const [c]))) + // cond: c >= 0 && int64(int16(c)) == c + // result: x + for { + x := v_0 + if x.Op != OpWasmI64And { + break + } + _ = x.Args[1] + x_1 := x.Args[1] + if x_1.Op != OpWasmI64Const { + break + } + c := auxIntToInt64(x_1.AuxInt) + if !(c >= 0 && int64(int16(c)) == c) { + break + } + v.copyOf(x) + return true + } return false } func rewriteValueWasm_OpWasmI64Extend32S(v *Value) bool { @@ -4172,6 +4192,26 @@ func rewriteValueWasm_OpWasmI64Extend32S(v *Value) bool { v.AddArg(x) return true } + // match: (I64Extend32S x:(I64And _ (I64Const [c]))) + // cond: c >= 0 && int64(int32(c)) == c + // result: x + for { + x := v_0 + if x.Op != OpWasmI64And { + break + } + _ = x.Args[1] + x_1 := x.Args[1] + if x_1.Op != OpWasmI64Const { + break + } + c := auxIntToInt64(x_1.AuxInt) + if !(c >= 0 && int64(int32(c)) == c) { + break + } + v.copyOf(x) + return true + } return false } func rewriteValueWasm_OpWasmI64Extend8S(v *Value) bool { @@ -4187,6 +4227,26 @@ func rewriteValueWasm_OpWasmI64Extend8S(v *Value) bool { v.AddArg(x) return true } + // match: (I64Extend8S x:(I64And _ (I64Const [c]))) + // cond: c >= 0 && int64(int8(c)) == c + // result: x + for { + x := v_0 + if x.Op != OpWasmI64And { + break + } + _ = x.Args[1] + x_1 := x.Args[1] + if x_1.Op != OpWasmI64Const { + break + } + c := auxIntToInt64(x_1.AuxInt) + if !(c >= 0 && int64(int8(c)) == c) { + break + } + v.copyOf(x) + return true + } return false } func rewriteValueWasm_OpWasmI64LeU(v *Value) bool {