cmd/compile: fold constants from lsh/rsh/lsh and rsh/lsh/rsh

Fixes #14825

Change-Id: Ib44d80579a55c15d75ea2ad1ef54efa6ca66a9a6
Reviewed-on: https://go-review.googlesource.com/20745
Run-TryBot: Todd Neal <todd@tneal.org>
Reviewed-by: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go
index 815468d..4ed4cbf 100644
--- a/src/cmd/compile/internal/ssa/rewritegeneric.go
+++ b/src/cmd/compile/internal/ssa/rewritegeneric.go
@@ -3420,6 +3420,39 @@
 func rewriteValuegeneric_OpLsh16x16(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
+	// match: (Lsh16x16 (Rsh16Ux16 (Lsh16x16 x (Const16 [c1])) (Const16 [c2])) (Const16 [c3]))
+	// cond: c1 >= c2 && c3 >= c2
+	// result: (Lsh16x16 x (Const16 <config.fe.TypeUInt16()> [c1-c2+c3]))
+	for {
+		if v.Args[0].Op != OpRsh16Ux16 {
+			break
+		}
+		if v.Args[0].Args[0].Op != OpLsh16x16 {
+			break
+		}
+		x := v.Args[0].Args[0].Args[0]
+		if v.Args[0].Args[0].Args[1].Op != OpConst16 {
+			break
+		}
+		c1 := v.Args[0].Args[0].Args[1].AuxInt
+		if v.Args[0].Args[1].Op != OpConst16 {
+			break
+		}
+		c2 := v.Args[0].Args[1].AuxInt
+		if v.Args[1].Op != OpConst16 {
+			break
+		}
+		c3 := v.Args[1].AuxInt
+		if !(c1 >= c2 && c3 >= c2) {
+			break
+		}
+		v.reset(OpLsh16x16)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpConst16, config.fe.TypeUInt16())
+		v0.AuxInt = c1 - c2 + c3
+		v.AddArg(v0)
+		return true
+	}
 	// match: (Lsh16x16  <t> x (Const16 [c]))
 	// cond:
 	// result: (Lsh16x64  x (Const64 <t> [int64(uint16(c))]))
@@ -3601,6 +3634,39 @@
 func rewriteValuegeneric_OpLsh32x32(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
+	// match: (Lsh32x32 (Rsh32Ux32 (Lsh32x32 x (Const32 [c1])) (Const32 [c2])) (Const32 [c3]))
+	// cond: c1 >= c2 && c3 >= c2
+	// result: (Lsh32x32 x (Const32 <config.fe.TypeUInt32()> [c1-c2+c3]))
+	for {
+		if v.Args[0].Op != OpRsh32Ux32 {
+			break
+		}
+		if v.Args[0].Args[0].Op != OpLsh32x32 {
+			break
+		}
+		x := v.Args[0].Args[0].Args[0]
+		if v.Args[0].Args[0].Args[1].Op != OpConst32 {
+			break
+		}
+		c1 := v.Args[0].Args[0].Args[1].AuxInt
+		if v.Args[0].Args[1].Op != OpConst32 {
+			break
+		}
+		c2 := v.Args[0].Args[1].AuxInt
+		if v.Args[1].Op != OpConst32 {
+			break
+		}
+		c3 := v.Args[1].AuxInt
+		if !(c1 >= c2 && c3 >= c2) {
+			break
+		}
+		v.reset(OpLsh32x32)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpConst32, config.fe.TypeUInt32())
+		v0.AuxInt = c1 - c2 + c3
+		v.AddArg(v0)
+		return true
+	}
 	// match: (Lsh32x32  <t> x (Const32 [c]))
 	// cond:
 	// result: (Lsh32x64  x (Const64 <t> [int64(uint32(c))]))
@@ -3840,6 +3906,39 @@
 		v.AuxInt = 0
 		return true
 	}
+	// match: (Lsh64x64 (Rsh64Ux64 (Lsh64x64 x (Const64 [c1])) (Const64 [c2])) (Const64 [c3]))
+	// cond: c1 >= c2 && c3 >= c2
+	// result: (Lsh64x64 x (Const64 <config.fe.TypeUInt64()> [c1-c2+c3]))
+	for {
+		if v.Args[0].Op != OpRsh64Ux64 {
+			break
+		}
+		if v.Args[0].Args[0].Op != OpLsh64x64 {
+			break
+		}
+		x := v.Args[0].Args[0].Args[0]
+		if v.Args[0].Args[0].Args[1].Op != OpConst64 {
+			break
+		}
+		c1 := v.Args[0].Args[0].Args[1].AuxInt
+		if v.Args[0].Args[1].Op != OpConst64 {
+			break
+		}
+		c2 := v.Args[0].Args[1].AuxInt
+		if v.Args[1].Op != OpConst64 {
+			break
+		}
+		c3 := v.Args[1].AuxInt
+		if !(c1 >= c2 && c3 >= c2) {
+			break
+		}
+		v.reset(OpLsh64x64)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v0.AuxInt = c1 - c2 + c3
+		v.AddArg(v0)
+		return true
+	}
 	// match: (Lsh64x64  x (Const64 [0]))
 	// cond:
 	// result: x
@@ -4090,6 +4189,39 @@
 func rewriteValuegeneric_OpLsh8x8(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
+	// match: (Lsh8x8 (Rsh8Ux8 (Lsh8x8 x (Const8 [c1])) (Const8 [c2])) (Const8 [c3]))
+	// cond: c1 >= c2 && c3 >= c2
+	// result: (Lsh8x8 x (Const8 <config.fe.TypeUInt8()> [c1-c2+c3]))
+	for {
+		if v.Args[0].Op != OpRsh8Ux8 {
+			break
+		}
+		if v.Args[0].Args[0].Op != OpLsh8x8 {
+			break
+		}
+		x := v.Args[0].Args[0].Args[0]
+		if v.Args[0].Args[0].Args[1].Op != OpConst8 {
+			break
+		}
+		c1 := v.Args[0].Args[0].Args[1].AuxInt
+		if v.Args[0].Args[1].Op != OpConst8 {
+			break
+		}
+		c2 := v.Args[0].Args[1].AuxInt
+		if v.Args[1].Op != OpConst8 {
+			break
+		}
+		c3 := v.Args[1].AuxInt
+		if !(c1 >= c2 && c3 >= c2) {
+			break
+		}
+		v.reset(OpLsh8x8)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpConst8, config.fe.TypeUInt8())
+		v0.AuxInt = c1 - c2 + c3
+		v.AddArg(v0)
+		return true
+	}
 	// match: (Lsh8x8   <t> x (Const8 [c]))
 	// cond:
 	// result: (Lsh8x64  x (Const64 <t> [int64(uint8(c))]))
@@ -5524,6 +5656,39 @@
 func rewriteValuegeneric_OpRsh16Ux16(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
+	// match: (Rsh16Ux16 (Lsh16x16 (Rsh16Ux16 x (Const16 [c1])) (Const16 [c2])) (Const16 [c3]))
+	// cond: c1 >= c2 && c3 >= c2
+	// result: (Rsh16Ux16 x (Const16 <config.fe.TypeUInt16()> [c1-c2+c3]))
+	for {
+		if v.Args[0].Op != OpLsh16x16 {
+			break
+		}
+		if v.Args[0].Args[0].Op != OpRsh16Ux16 {
+			break
+		}
+		x := v.Args[0].Args[0].Args[0]
+		if v.Args[0].Args[0].Args[1].Op != OpConst16 {
+			break
+		}
+		c1 := v.Args[0].Args[0].Args[1].AuxInt
+		if v.Args[0].Args[1].Op != OpConst16 {
+			break
+		}
+		c2 := v.Args[0].Args[1].AuxInt
+		if v.Args[1].Op != OpConst16 {
+			break
+		}
+		c3 := v.Args[1].AuxInt
+		if !(c1 >= c2 && c3 >= c2) {
+			break
+		}
+		v.reset(OpRsh16Ux16)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpConst16, config.fe.TypeUInt16())
+		v0.AuxInt = c1 - c2 + c3
+		v.AddArg(v0)
+		return true
+	}
 	// match: (Rsh16Ux16 <t> x (Const16 [c]))
 	// cond:
 	// result: (Rsh16Ux64 x (Const64 <t> [int64(uint16(c))]))
@@ -5849,6 +6014,39 @@
 func rewriteValuegeneric_OpRsh32Ux32(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
+	// match: (Rsh32Ux32 (Lsh32x32 (Rsh32Ux32 x (Const32 [c1])) (Const32 [c2])) (Const32 [c3]))
+	// cond: c1 >= c2 && c3 >= c2
+	// result: (Rsh32Ux32 x (Const32 <config.fe.TypeUInt32()> [c1-c2+c3]))
+	for {
+		if v.Args[0].Op != OpLsh32x32 {
+			break
+		}
+		if v.Args[0].Args[0].Op != OpRsh32Ux32 {
+			break
+		}
+		x := v.Args[0].Args[0].Args[0]
+		if v.Args[0].Args[0].Args[1].Op != OpConst32 {
+			break
+		}
+		c1 := v.Args[0].Args[0].Args[1].AuxInt
+		if v.Args[0].Args[1].Op != OpConst32 {
+			break
+		}
+		c2 := v.Args[0].Args[1].AuxInt
+		if v.Args[1].Op != OpConst32 {
+			break
+		}
+		c3 := v.Args[1].AuxInt
+		if !(c1 >= c2 && c3 >= c2) {
+			break
+		}
+		v.reset(OpRsh32Ux32)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpConst32, config.fe.TypeUInt32())
+		v0.AuxInt = c1 - c2 + c3
+		v.AddArg(v0)
+		return true
+	}
 	// match: (Rsh32Ux32 <t> x (Const32 [c]))
 	// cond:
 	// result: (Rsh32Ux64 x (Const64 <t> [int64(uint32(c))]))
@@ -6232,6 +6430,39 @@
 		v.AuxInt = 0
 		return true
 	}
+	// match: (Rsh64Ux64 (Lsh64x64 (Rsh64Ux64 x (Const64 [c1])) (Const64 [c2])) (Const64 [c3]))
+	// cond: c1 >= c2 && c3 >= c2
+	// result: (Rsh64Ux64 x (Const64 <config.fe.TypeUInt64()> [c1-c2+c3]))
+	for {
+		if v.Args[0].Op != OpLsh64x64 {
+			break
+		}
+		if v.Args[0].Args[0].Op != OpRsh64Ux64 {
+			break
+		}
+		x := v.Args[0].Args[0].Args[0]
+		if v.Args[0].Args[0].Args[1].Op != OpConst64 {
+			break
+		}
+		c1 := v.Args[0].Args[0].Args[1].AuxInt
+		if v.Args[0].Args[1].Op != OpConst64 {
+			break
+		}
+		c2 := v.Args[0].Args[1].AuxInt
+		if v.Args[1].Op != OpConst64 {
+			break
+		}
+		c3 := v.Args[1].AuxInt
+		if !(c1 >= c2 && c3 >= c2) {
+			break
+		}
+		v.reset(OpRsh64Ux64)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v0.AuxInt = c1 - c2 + c3
+		v.AddArg(v0)
+		return true
+	}
 	// match: (Rsh64Ux64 x (Const64 [0]))
 	// cond:
 	// result: x
@@ -6682,6 +6913,39 @@
 func rewriteValuegeneric_OpRsh8Ux8(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
+	// match: (Rsh8Ux8 (Lsh8x8 (Rsh8Ux8 x (Const8 [c1])) (Const8 [c2])) (Const8 [c3]))
+	// cond: c1 >= c2 && c3 >= c2
+	// result: (Rsh8Ux8 x (Const8 <config.fe.TypeUInt8()> [c1-c2+c3]))
+	for {
+		if v.Args[0].Op != OpLsh8x8 {
+			break
+		}
+		if v.Args[0].Args[0].Op != OpRsh8Ux8 {
+			break
+		}
+		x := v.Args[0].Args[0].Args[0]
+		if v.Args[0].Args[0].Args[1].Op != OpConst8 {
+			break
+		}
+		c1 := v.Args[0].Args[0].Args[1].AuxInt
+		if v.Args[0].Args[1].Op != OpConst8 {
+			break
+		}
+		c2 := v.Args[0].Args[1].AuxInt
+		if v.Args[1].Op != OpConst8 {
+			break
+		}
+		c3 := v.Args[1].AuxInt
+		if !(c1 >= c2 && c3 >= c2) {
+			break
+		}
+		v.reset(OpRsh8Ux8)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpConst8, config.fe.TypeUInt8())
+		v0.AuxInt = c1 - c2 + c3
+		v.AddArg(v0)
+		return true
+	}
 	// match: (Rsh8Ux8  <t> x (Const8 [c]))
 	// cond:
 	// result: (Rsh8Ux64 x (Const64 <t> [int64(uint8(c))]))