From def4e491be636d5fda1f1d36bd17fbf3f8a37ea7 Mon Sep 17 00:00:00 2001 From: George Adams Date: Mon, 9 Mar 2026 08:56:37 +0000 Subject: [PATCH] cmd/compile: add identity and absorption rules for wasm Add post-lowering identity and absorption rules for I64And, I64Or, I64Xor, and I64Mul with constant operands: (I64And x (I64Const [-1])) => x (I64And x (I64Const [0])) => (I64Const [0]) (I64Or x (I64Const [0])) => x (I64Or x (I64Const [-1])) => (I64Const [-1]) (I64Xor x (I64Const [0])) => x (I64Mul x (I64Const [0])) => (I64Const [0]) (I64Mul x (I64Const [1])) => x The generic SSA rules handle these patterns before lowering, but these rules catch cases where wasm-specific lowering or other post-lowering optimization passes produce new nodes with identity or absorbing constant operands. For example, the complement rule lowers Com64(x) to (I64Xor x (I64Const [-1])), and if x is later determined to be all-ones, the I64And absorption rule can fold the result to zero. Cq-Include-Trybots: luci.golang.try:gotip-wasip1-wasm_wasmtime,gotip-wasip1-wasm_wazero Change-Id: Ie9a40e075662d4828a70e30b258d92ee171d0bc2 Reviewed-on: https://go-review.googlesource.com/c/go/+/752861 LUCI-TryBot-Result: Go LUCI Auto-Submit: Keith Randall Reviewed-by: Keith Randall Reviewed-by: Keith Randall Reviewed-by: David Chase --- src/cmd/compile/internal/ssa/_gen/Wasm.rules | 10 +++ src/cmd/compile/internal/ssa/rewriteWasm.go | 70 ++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/src/cmd/compile/internal/ssa/_gen/Wasm.rules b/src/cmd/compile/internal/ssa/_gen/Wasm.rules index 8490fb39b8..9b582c8c00 100644 --- a/src/cmd/compile/internal/ssa/_gen/Wasm.rules +++ b/src/cmd/compile/internal/ssa/_gen/Wasm.rules @@ -390,6 +390,16 @@ (I64ShrU (I64Const [x]) (I64Const [y])) => (I64Const [int64(uint64(x) >> uint64(y))]) (I64ShrS (I64Const [x]) (I64Const [y])) => (I64Const [x >> uint64(y)]) +// Identity and absorption rules for AND/OR/XOR/MUL. +// These fire on nodes created during or after lowering. +(I64And x (I64Const [-1])) => x +(I64And x (I64Const [0])) => (I64Const [0]) +(I64Or x (I64Const [0])) => x +(I64Or x (I64Const [-1])) => (I64Const [-1]) +(I64Xor x (I64Const [0])) => x +(I64Mul x (I64Const [0])) => (I64Const [0]) +(I64Mul x (I64Const [1])) => 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 68a40f0c7f..c0ccd9611d 100644 --- a/src/cmd/compile/internal/ssa/rewriteWasm.go +++ b/src/cmd/compile/internal/ssa/rewriteWasm.go @@ -3949,6 +3949,26 @@ func rewriteValueWasm_OpWasmI64And(v *Value) bool { v.AuxInt = int64ToAuxInt(x & y) return true } + // match: (I64And x (I64Const [-1])) + // result: x + for { + x := v_0 + if v_1.Op != OpWasmI64Const || auxIntToInt64(v_1.AuxInt) != -1 { + break + } + v.copyOf(x) + return true + } + // match: (I64And x (I64Const [0])) + // result: (I64Const [0]) + for { + if v_1.Op != OpWasmI64Const || auxIntToInt64(v_1.AuxInt) != 0 { + break + } + v.reset(OpWasmI64Const) + v.AuxInt = int64ToAuxInt(0) + return true + } // match: (I64And (I64Const [x]) y) // cond: y.Op != OpWasmI64Const // result: (I64And y (I64Const [x])) @@ -4448,6 +4468,26 @@ func rewriteValueWasm_OpWasmI64Mul(v *Value) bool { v.AuxInt = int64ToAuxInt(x * y) return true } + // match: (I64Mul x (I64Const [0])) + // result: (I64Const [0]) + for { + if v_1.Op != OpWasmI64Const || auxIntToInt64(v_1.AuxInt) != 0 { + break + } + v.reset(OpWasmI64Const) + v.AuxInt = int64ToAuxInt(0) + return true + } + // match: (I64Mul x (I64Const [1])) + // result: x + for { + x := v_0 + if v_1.Op != OpWasmI64Const || auxIntToInt64(v_1.AuxInt) != 1 { + break + } + v.copyOf(x) + return true + } // match: (I64Mul (I64Const [x]) y) // cond: y.Op != OpWasmI64Const // result: (I64Mul y (I64Const [x])) @@ -4564,6 +4604,26 @@ func rewriteValueWasm_OpWasmI64Or(v *Value) bool { v.AuxInt = int64ToAuxInt(x | y) return true } + // match: (I64Or x (I64Const [0])) + // result: x + for { + x := v_0 + if v_1.Op != OpWasmI64Const || auxIntToInt64(v_1.AuxInt) != 0 { + break + } + v.copyOf(x) + return true + } + // match: (I64Or x (I64Const [-1])) + // result: (I64Const [-1]) + for { + if v_1.Op != OpWasmI64Const || auxIntToInt64(v_1.AuxInt) != -1 { + break + } + v.reset(OpWasmI64Const) + v.AuxInt = int64ToAuxInt(-1) + return true + } // match: (I64Or (I64Const [x]) y) // cond: y.Op != OpWasmI64Const // result: (I64Or y (I64Const [x])) @@ -4788,6 +4848,16 @@ func rewriteValueWasm_OpWasmI64Xor(v *Value) bool { v.AuxInt = int64ToAuxInt(x ^ y) return true } + // match: (I64Xor x (I64Const [0])) + // result: x + for { + x := v_0 + if v_1.Op != OpWasmI64Const || auxIntToInt64(v_1.AuxInt) != 0 { + break + } + v.copyOf(x) + return true + } // match: (I64Xor (I64Const [x]) y) // cond: y.Op != OpWasmI64Const // result: (I64Xor y (I64Const [x]))