&& uint64(c1) >= uint64(c2) && uint64(c3) >= uint64(c2) && !uaddOvf(c1-c2, c3)
-> (Lsh8x64 x (Const64 <config.fe.TypeUInt64()> [c1-c2+c3]))
+// replace shifts with zero extensions
+(Rsh16Ux64 (Lsh16x64 x (Const64 [8])) (Const64 [8])) -> (ZeroExt8to16 (Trunc16to8 <config.fe.TypeUInt8()> x))
+(Rsh32Ux64 (Lsh32x64 x (Const64 [24])) (Const64 [24])) -> (ZeroExt8to32 (Trunc32to8 <config.fe.TypeUInt8()> x))
+(Rsh64Ux64 (Lsh64x64 x (Const64 [56])) (Const64 [56])) -> (ZeroExt8to64 (Trunc64to8 <config.fe.TypeUInt8()> x))
+(Rsh32Ux64 (Lsh32x64 x (Const64 [16])) (Const64 [16])) -> (ZeroExt16to32 (Trunc32to16 <config.fe.TypeUInt16()> x))
+(Rsh64Ux64 (Lsh64x64 x (Const64 [48])) (Const64 [48])) -> (ZeroExt16to64 (Trunc64to16 <config.fe.TypeUInt16()> x))
+(Rsh64Ux64 (Lsh64x64 x (Const64 [32])) (Const64 [32])) -> (ZeroExt32to64 (Trunc64to32 <config.fe.TypeUInt32()> x))
+
+// replace shifts with sign extensions
+(Rsh16x64 (Lsh16x64 x (Const64 [8])) (Const64 [8])) -> (SignExt8to16 (Trunc16to8 <config.fe.TypeInt8()> x))
+(Rsh32x64 (Lsh32x64 x (Const64 [24])) (Const64 [24])) -> (SignExt8to32 (Trunc32to8 <config.fe.TypeInt8()> x))
+(Rsh64x64 (Lsh64x64 x (Const64 [56])) (Const64 [56])) -> (SignExt8to64 (Trunc64to8 <config.fe.TypeInt8()> x))
+(Rsh32x64 (Lsh32x64 x (Const64 [16])) (Const64 [16])) -> (SignExt16to32 (Trunc32to16 <config.fe.TypeInt16()> x))
+(Rsh64x64 (Lsh64x64 x (Const64 [48])) (Const64 [48])) -> (SignExt16to64 (Trunc64to16 <config.fe.TypeInt16()> x))
+(Rsh64x64 (Lsh64x64 x (Const64 [32])) (Const64 [32])) -> (SignExt32to64 (Trunc64to32 <config.fe.TypeInt32()> x))
+
// constant comparisons
(Eq64 (Const64 [c]) (Const64 [d])) -> (ConstBool [b2i(c == d)])
(Eq32 (Const32 [c]) (Const32 [d])) -> (ConstBool [b2i(c == d)])
v.AddArg(v0)
return true
}
+ // match: (Rsh16Ux64 (Lsh16x64 x (Const64 [8])) (Const64 [8]))
+ // cond:
+ // result: (ZeroExt8to16 (Trunc16to8 <config.fe.TypeUInt8()> x))
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpLsh16x64 {
+ break
+ }
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpConst64 {
+ break
+ }
+ if v_0_1.AuxInt != 8 {
+ break
+ }
+ v_1 := v.Args[1]
+ if v_1.Op != OpConst64 {
+ break
+ }
+ if v_1.AuxInt != 8 {
+ break
+ }
+ v.reset(OpZeroExt8to16)
+ v0 := b.NewValue0(v.Pos, OpTrunc16to8, config.fe.TypeUInt8())
+ v0.AddArg(x)
+ v.AddArg(v0)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpRsh16Ux8(v *Value, config *Config) bool {
v.AddArg(v0)
return true
}
+ // match: (Rsh16x64 (Lsh16x64 x (Const64 [8])) (Const64 [8]))
+ // cond:
+ // result: (SignExt8to16 (Trunc16to8 <config.fe.TypeInt8()> x))
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpLsh16x64 {
+ break
+ }
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpConst64 {
+ break
+ }
+ if v_0_1.AuxInt != 8 {
+ break
+ }
+ v_1 := v.Args[1]
+ if v_1.Op != OpConst64 {
+ break
+ }
+ if v_1.AuxInt != 8 {
+ break
+ }
+ v.reset(OpSignExt8to16)
+ v0 := b.NewValue0(v.Pos, OpTrunc16to8, config.fe.TypeInt8())
+ v0.AddArg(x)
+ v.AddArg(v0)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpRsh16x8(v *Value, config *Config) bool {
v.AddArg(v0)
return true
}
+ // match: (Rsh32Ux64 (Lsh32x64 x (Const64 [24])) (Const64 [24]))
+ // cond:
+ // result: (ZeroExt8to32 (Trunc32to8 <config.fe.TypeUInt8()> x))
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpLsh32x64 {
+ break
+ }
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpConst64 {
+ break
+ }
+ if v_0_1.AuxInt != 24 {
+ break
+ }
+ v_1 := v.Args[1]
+ if v_1.Op != OpConst64 {
+ break
+ }
+ if v_1.AuxInt != 24 {
+ break
+ }
+ v.reset(OpZeroExt8to32)
+ v0 := b.NewValue0(v.Pos, OpTrunc32to8, config.fe.TypeUInt8())
+ v0.AddArg(x)
+ v.AddArg(v0)
+ return true
+ }
+ // match: (Rsh32Ux64 (Lsh32x64 x (Const64 [16])) (Const64 [16]))
+ // cond:
+ // result: (ZeroExt16to32 (Trunc32to16 <config.fe.TypeUInt16()> x))
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpLsh32x64 {
+ break
+ }
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpConst64 {
+ break
+ }
+ if v_0_1.AuxInt != 16 {
+ break
+ }
+ v_1 := v.Args[1]
+ if v_1.Op != OpConst64 {
+ break
+ }
+ if v_1.AuxInt != 16 {
+ break
+ }
+ v.reset(OpZeroExt16to32)
+ v0 := b.NewValue0(v.Pos, OpTrunc32to16, config.fe.TypeUInt16())
+ v0.AddArg(x)
+ v.AddArg(v0)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpRsh32Ux8(v *Value, config *Config) bool {
v.AddArg(v0)
return true
}
+ // match: (Rsh32x64 (Lsh32x64 x (Const64 [24])) (Const64 [24]))
+ // cond:
+ // result: (SignExt8to32 (Trunc32to8 <config.fe.TypeInt8()> x))
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpLsh32x64 {
+ break
+ }
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpConst64 {
+ break
+ }
+ if v_0_1.AuxInt != 24 {
+ break
+ }
+ v_1 := v.Args[1]
+ if v_1.Op != OpConst64 {
+ break
+ }
+ if v_1.AuxInt != 24 {
+ break
+ }
+ v.reset(OpSignExt8to32)
+ v0 := b.NewValue0(v.Pos, OpTrunc32to8, config.fe.TypeInt8())
+ v0.AddArg(x)
+ v.AddArg(v0)
+ return true
+ }
+ // match: (Rsh32x64 (Lsh32x64 x (Const64 [16])) (Const64 [16]))
+ // cond:
+ // result: (SignExt16to32 (Trunc32to16 <config.fe.TypeInt16()> x))
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpLsh32x64 {
+ break
+ }
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpConst64 {
+ break
+ }
+ if v_0_1.AuxInt != 16 {
+ break
+ }
+ v_1 := v.Args[1]
+ if v_1.Op != OpConst64 {
+ break
+ }
+ if v_1.AuxInt != 16 {
+ break
+ }
+ v.reset(OpSignExt16to32)
+ v0 := b.NewValue0(v.Pos, OpTrunc32to16, config.fe.TypeInt16())
+ v0.AddArg(x)
+ v.AddArg(v0)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpRsh32x8(v *Value, config *Config) bool {
v.AddArg(v0)
return true
}
+ // match: (Rsh64Ux64 (Lsh64x64 x (Const64 [56])) (Const64 [56]))
+ // cond:
+ // result: (ZeroExt8to64 (Trunc64to8 <config.fe.TypeUInt8()> x))
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpLsh64x64 {
+ break
+ }
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpConst64 {
+ break
+ }
+ if v_0_1.AuxInt != 56 {
+ break
+ }
+ v_1 := v.Args[1]
+ if v_1.Op != OpConst64 {
+ break
+ }
+ if v_1.AuxInt != 56 {
+ break
+ }
+ v.reset(OpZeroExt8to64)
+ v0 := b.NewValue0(v.Pos, OpTrunc64to8, config.fe.TypeUInt8())
+ v0.AddArg(x)
+ v.AddArg(v0)
+ return true
+ }
+ // match: (Rsh64Ux64 (Lsh64x64 x (Const64 [48])) (Const64 [48]))
+ // cond:
+ // result: (ZeroExt16to64 (Trunc64to16 <config.fe.TypeUInt16()> x))
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpLsh64x64 {
+ break
+ }
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpConst64 {
+ break
+ }
+ if v_0_1.AuxInt != 48 {
+ break
+ }
+ v_1 := v.Args[1]
+ if v_1.Op != OpConst64 {
+ break
+ }
+ if v_1.AuxInt != 48 {
+ break
+ }
+ v.reset(OpZeroExt16to64)
+ v0 := b.NewValue0(v.Pos, OpTrunc64to16, config.fe.TypeUInt16())
+ v0.AddArg(x)
+ v.AddArg(v0)
+ return true
+ }
+ // match: (Rsh64Ux64 (Lsh64x64 x (Const64 [32])) (Const64 [32]))
+ // cond:
+ // result: (ZeroExt32to64 (Trunc64to32 <config.fe.TypeUInt32()> x))
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpLsh64x64 {
+ break
+ }
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpConst64 {
+ break
+ }
+ if v_0_1.AuxInt != 32 {
+ break
+ }
+ v_1 := v.Args[1]
+ if v_1.Op != OpConst64 {
+ break
+ }
+ if v_1.AuxInt != 32 {
+ break
+ }
+ v.reset(OpZeroExt32to64)
+ v0 := b.NewValue0(v.Pos, OpTrunc64to32, config.fe.TypeUInt32())
+ v0.AddArg(x)
+ v.AddArg(v0)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpRsh64Ux8(v *Value, config *Config) bool {
v.AddArg(v0)
return true
}
+ // match: (Rsh64x64 (Lsh64x64 x (Const64 [56])) (Const64 [56]))
+ // cond:
+ // result: (SignExt8to64 (Trunc64to8 <config.fe.TypeInt8()> x))
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpLsh64x64 {
+ break
+ }
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpConst64 {
+ break
+ }
+ if v_0_1.AuxInt != 56 {
+ break
+ }
+ v_1 := v.Args[1]
+ if v_1.Op != OpConst64 {
+ break
+ }
+ if v_1.AuxInt != 56 {
+ break
+ }
+ v.reset(OpSignExt8to64)
+ v0 := b.NewValue0(v.Pos, OpTrunc64to8, config.fe.TypeInt8())
+ v0.AddArg(x)
+ v.AddArg(v0)
+ return true
+ }
+ // match: (Rsh64x64 (Lsh64x64 x (Const64 [48])) (Const64 [48]))
+ // cond:
+ // result: (SignExt16to64 (Trunc64to16 <config.fe.TypeInt16()> x))
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpLsh64x64 {
+ break
+ }
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpConst64 {
+ break
+ }
+ if v_0_1.AuxInt != 48 {
+ break
+ }
+ v_1 := v.Args[1]
+ if v_1.Op != OpConst64 {
+ break
+ }
+ if v_1.AuxInt != 48 {
+ break
+ }
+ v.reset(OpSignExt16to64)
+ v0 := b.NewValue0(v.Pos, OpTrunc64to16, config.fe.TypeInt16())
+ v0.AddArg(x)
+ v.AddArg(v0)
+ return true
+ }
+ // match: (Rsh64x64 (Lsh64x64 x (Const64 [32])) (Const64 [32]))
+ // cond:
+ // result: (SignExt32to64 (Trunc64to32 <config.fe.TypeInt32()> x))
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpLsh64x64 {
+ break
+ }
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpConst64 {
+ break
+ }
+ if v_0_1.AuxInt != 32 {
+ break
+ }
+ v_1 := v.Args[1]
+ if v_1.Op != OpConst64 {
+ break
+ }
+ if v_1.AuxInt != 32 {
+ break
+ }
+ v.reset(OpSignExt32to64)
+ v0 := b.NewValue0(v.Pos, OpTrunc64to32, config.fe.TypeInt32())
+ v0.AddArg(x)
+ v.AddArg(v0)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpRsh64x8(v *Value, config *Config) bool {
Compile(fun.f)
return fun
}
+
+func TestShiftToExtensionAMD64(t *testing.T) {
+ // Test that eligible pairs of constant shifts are converted to extensions.
+ // For example:
+ // (uint64(x) << 32) >> 32 -> uint64(uint32(x))
+ ops := map[Op]int{
+ OpAMD64SHLQconst: 0, OpAMD64SHLLconst: 0,
+ OpAMD64SHRQconst: 0, OpAMD64SHRLconst: 0,
+ OpAMD64SARQconst: 0, OpAMD64SARLconst: 0,
+ }
+ tests := [...]struct {
+ amount int64
+ left, right Op
+ typ Type
+ }{
+ // unsigned
+ {56, OpLsh64x64, OpRsh64Ux64, TypeUInt64},
+ {48, OpLsh64x64, OpRsh64Ux64, TypeUInt64},
+ {32, OpLsh64x64, OpRsh64Ux64, TypeUInt64},
+ {24, OpLsh32x64, OpRsh32Ux64, TypeUInt32},
+ {16, OpLsh32x64, OpRsh32Ux64, TypeUInt32},
+ {8, OpLsh16x64, OpRsh16Ux64, TypeUInt16},
+ // signed
+ {56, OpLsh64x64, OpRsh64x64, TypeInt64},
+ {48, OpLsh64x64, OpRsh64x64, TypeInt64},
+ {32, OpLsh64x64, OpRsh64x64, TypeInt64},
+ {24, OpLsh32x64, OpRsh32x64, TypeInt32},
+ {16, OpLsh32x64, OpRsh32x64, TypeInt32},
+ {8, OpLsh16x64, OpRsh16x64, TypeInt16},
+ }
+ c := testConfig(t)
+ for _, tc := range tests {
+ fun := makeShiftExtensionFunc(c, tc.amount, tc.left, tc.right, tc.typ)
+ checkOpcodeCounts(t, fun.f, ops)
+ fun.f.Free()
+ }
+}
+
+// makeShiftExtensionFunc generates a function containing:
+//
+// (rshift (lshift (Const64 [amount])) (Const64 [amount]))
+//
+// This may be equivalent to a sign or zero extension.
+func makeShiftExtensionFunc(c *Config, amount int64, lshift, rshift Op, typ Type) fun {
+ ptyp := &TypeImpl{Size_: 8, Ptr: true, Name: "ptr"}
+ fun := Fun(c, "entry",
+ Bloc("entry",
+ Valu("mem", OpInitMem, TypeMem, 0, nil),
+ Valu("SP", OpSP, TypeUInt64, 0, nil),
+ Valu("argptr", OpOffPtr, ptyp, 8, nil, "SP"),
+ Valu("resptr", OpOffPtr, ptyp, 16, nil, "SP"),
+ Valu("load", OpLoad, typ, 0, nil, "argptr", "mem"),
+ Valu("c", OpConst64, TypeUInt64, amount, nil),
+ Valu("lshift", lshift, typ, 0, nil, "load", "c"),
+ Valu("rshift", rshift, typ, 0, nil, "lshift", "c"),
+ Valu("store", OpStore, TypeMem, 8, nil, "resptr", "rshift", "mem"),
+ Exit("store")))
+ Compile(fun.f)
+ return fun
+}