Commit 21831355 authored by Josh Bleecher Snyder's avatar Josh Bleecher Snyder

cmd/compile: recognize bit test patterns on amd64

Updates #18943

Change-Id: If3080d6133bb6d2710b57294da24c90251ab4e08
Reviewed-on: https://go-review.googlesource.com/36329
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarKeith Randall <khr@golang.org>
parent ac7761e1
...@@ -72,6 +72,8 @@ var progtable = [x86.ALAST & obj.AMask]gc.ProgInfo{ ...@@ -72,6 +72,8 @@ var progtable = [x86.ALAST & obj.AMask]gc.ProgInfo{
x86.ACMOVWEQ & obj.AMask: {Flags: gc.SizeW | gc.LeftRead | RightRdwr | gc.UseCarry}, x86.ACMOVWEQ & obj.AMask: {Flags: gc.SizeW | gc.LeftRead | RightRdwr | gc.UseCarry},
x86.ACMOVWNE & obj.AMask: {Flags: gc.SizeW | gc.LeftRead | RightRdwr | gc.UseCarry}, x86.ACMOVWNE & obj.AMask: {Flags: gc.SizeW | gc.LeftRead | RightRdwr | gc.UseCarry},
x86.ABTL & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightRead | gc.SetCarry},
x86.ABTQ & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightRead | gc.SetCarry},
x86.ACMPB & obj.AMask: {Flags: gc.SizeB | gc.LeftRead | gc.RightRead | gc.SetCarry}, x86.ACMPB & obj.AMask: {Flags: gc.SizeB | gc.LeftRead | gc.RightRead | gc.SetCarry},
x86.ACMPL & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightRead | gc.SetCarry}, x86.ACMPL & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightRead | gc.SetCarry},
x86.ACMPQ & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightRead | gc.SetCarry}, x86.ACMPQ & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightRead | gc.SetCarry},
......
...@@ -471,7 +471,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { ...@@ -471,7 +471,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg() p.To.Reg = v.Reg()
case ssa.OpAMD64CMPQ, ssa.OpAMD64CMPL, ssa.OpAMD64CMPW, ssa.OpAMD64CMPB, case ssa.OpAMD64CMPQ, ssa.OpAMD64CMPL, ssa.OpAMD64CMPW, ssa.OpAMD64CMPB,
ssa.OpAMD64TESTQ, ssa.OpAMD64TESTL, ssa.OpAMD64TESTW, ssa.OpAMD64TESTB: ssa.OpAMD64TESTQ, ssa.OpAMD64TESTL, ssa.OpAMD64TESTW, ssa.OpAMD64TESTB,
ssa.OpAMD64BTL, ssa.OpAMD64BTQ:
opregreg(v.Op.Asm(), v.Args[1].Reg(), v.Args[0].Reg()) opregreg(v.Op.Asm(), v.Args[1].Reg(), v.Args[0].Reg())
case ssa.OpAMD64UCOMISS, ssa.OpAMD64UCOMISD: case ssa.OpAMD64UCOMISS, ssa.OpAMD64UCOMISD:
// Go assembler has swapped operands for UCOMISx relative to CMP, // Go assembler has swapped operands for UCOMISx relative to CMP,
...@@ -483,7 +484,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { ...@@ -483,7 +484,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.From.Reg = v.Args[0].Reg() p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_CONST p.To.Type = obj.TYPE_CONST
p.To.Offset = v.AuxInt p.To.Offset = v.AuxInt
case ssa.OpAMD64TESTQconst, ssa.OpAMD64TESTLconst, ssa.OpAMD64TESTWconst, ssa.OpAMD64TESTBconst: case ssa.OpAMD64TESTQconst, ssa.OpAMD64TESTLconst, ssa.OpAMD64TESTWconst, ssa.OpAMD64TESTBconst,
ssa.OpAMD64BTLconst, ssa.OpAMD64BTQconst:
p := gc.Prog(v.Op.Asm()) p := gc.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_CONST p.From.Type = obj.TYPE_CONST
p.From.Offset = v.AuxInt p.From.Offset = v.AuxInt
......
...@@ -504,6 +504,45 @@ var linuxAMD64Tests = []*asmTest{ ...@@ -504,6 +504,45 @@ var linuxAMD64Tests = []*asmTest{
`, `,
[]string{"\"abc\""}, []string{"\"abc\""},
}, },
// Bit test ops on amd64, issue 18943.
{
`
func f37(a, b uint64) int {
if a&(1<<(b&63)) != 0 {
return 1
}
return -1
}
`,
[]string{"\tBTQ\t"},
},
{
`
func f38(a, b uint64) bool {
return a&(1<<(b&63)) != 0
}
`,
[]string{"\tBTQ\t"},
},
{
`
func f39(a uint64) int {
if a&(1<<60) != 0 {
return 1
}
return -1
}
`,
[]string{"\tBTQ\t\\$60"},
},
{
`
func f40(a uint64) bool {
return a&(1<<60) != 0
}
`,
[]string{"\tBTQ\t\\$60"},
},
} }
var linux386Tests = []*asmTest{ var linux386Tests = []*asmTest{
......
...@@ -518,6 +518,37 @@ ...@@ -518,6 +518,37 @@
(NE (TESTB (SETA cmp) (SETA cmp)) yes no) -> (UGT cmp yes no) (NE (TESTB (SETA cmp) (SETA cmp)) yes no) -> (UGT cmp yes no)
(NE (TESTB (SETAE cmp) (SETAE cmp)) yes no) -> (UGE cmp yes no) (NE (TESTB (SETAE cmp) (SETAE cmp)) yes no) -> (UGE cmp yes no)
// Normalize TESTx argument order for BTx rewrites below.
(TESTQ y x:(SHLQ _ _)) && y.Op != OpAMD64SHLQ -> (TESTQ x y)
(TESTL y x:(SHLL _ _)) && y.Op != OpAMD64SHLL -> (TESTL x y)
// Recognize bit tests: a&(1<<b) != 0 for b suitably bounded
// Note that ULT and SETB check the carry flag; they are identical to CS and SETCS.
// Same, mutatis mutandis, for UGE and SETAE, and CC and SETCC.
(NE (TESTL (SHLL (MOVLconst [1]) x) y)) && !config.nacl -> (ULT (BTL x y))
(EQ (TESTL (SHLL (MOVLconst [1]) x) y)) && !config.nacl -> (UGE (BTL x y))
(NE (TESTQ (SHLQ (MOVQconst [1]) x) y)) && !config.nacl -> (ULT (BTQ x y))
(EQ (TESTQ (SHLQ (MOVQconst [1]) x) y)) && !config.nacl -> (UGE (BTQ x y))
(NE (TESTLconst [c] x)) && isPowerOfTwo(c) && log2(c) < 32 && !config.nacl -> (ULT (BTLconst [log2(c)] x))
(EQ (TESTLconst [c] x)) && isPowerOfTwo(c) && log2(c) < 32 && !config.nacl -> (UGE (BTLconst [log2(c)] x))
(NE (TESTQconst [c] x)) && isPowerOfTwo(c) && log2(c) < 64 && !config.nacl -> (ULT (BTQconst [log2(c)] x))
(EQ (TESTQconst [c] x)) && isPowerOfTwo(c) && log2(c) < 64 && !config.nacl -> (UGE (BTQconst [log2(c)] x))
(NE (TESTQ (MOVQconst [c]) x)) && isPowerOfTwo(c) && log2(c) < 64 && !config.nacl -> (ULT (BTQconst [log2(c)] x))
(EQ (TESTQ (MOVQconst [c]) x)) && isPowerOfTwo(c) && log2(c) < 64 && !config.nacl -> (UGE (BTQconst [log2(c)] x))
(SETNE (TESTL (SHLL (MOVLconst [1]) x) y)) && !config.nacl -> (SETB (BTL x y))
(SETEQ (TESTL (SHLL (MOVLconst [1]) x) y)) && !config.nacl -> (SETAE (BTL x y))
(SETNE (TESTQ (SHLQ (MOVQconst [1]) x) y)) && !config.nacl -> (SETB (BTQ x y))
(SETEQ (TESTQ (SHLQ (MOVQconst [1]) x) y)) && !config.nacl -> (SETAE (BTQ x y))
(SETNE (TESTLconst [c] x)) && isPowerOfTwo(c) && log2(c) < 32 && !config.nacl -> (SETB (BTLconst [log2(c)] x))
(SETEQ (TESTLconst [c] x)) && isPowerOfTwo(c) && log2(c) < 32 && !config.nacl -> (SETAE (BTLconst [log2(c)] x))
(SETNE (TESTQconst [c] x)) && isPowerOfTwo(c) && log2(c) < 64 && !config.nacl -> (SETB (BTQconst [log2(c)] x))
(SETEQ (TESTQconst [c] x)) && isPowerOfTwo(c) && log2(c) < 64 && !config.nacl -> (SETAE (BTQconst [log2(c)] x))
(SETNE (TESTQ (MOVQconst [c]) x)) && isPowerOfTwo(c) && log2(c) < 64 && !config.nacl -> (SETB (BTQconst [log2(c)] x))
(SETEQ (TESTQ (MOVQconst [c]) x)) && isPowerOfTwo(c) && log2(c) < 64 && !config.nacl -> (SETAE (BTQconst [log2(c)] x))
// Convert BTQconst to BTLconst if possible. It has a shorter encoding.
(BTQconst [c] x) && c < 32 -> (BTLconst [c] x)
// Special case for floating point - LF/LEF not generated // Special case for floating point - LF/LEF not generated
(NE (TESTB (SETGF cmp) (SETGF cmp)) yes no) -> (UGT cmp yes no) (NE (TESTB (SETGF cmp) (SETGF cmp)) yes no) -> (UGT cmp yes no)
(NE (TESTB (SETGEF cmp) (SETGEF cmp)) yes no) -> (UGE cmp yes no) (NE (TESTB (SETGEF cmp) (SETGEF cmp)) yes no) -> (UGE cmp yes no)
...@@ -1380,6 +1411,16 @@ ...@@ -1380,6 +1411,16 @@
(CMPWconst (ANDLconst [c] x) [0]) -> (TESTWconst [int64(int16(c))] x) (CMPWconst (ANDLconst [c] x) [0]) -> (TESTWconst [int64(int16(c))] x)
(CMPBconst (ANDLconst [c] x) [0]) -> (TESTBconst [int64(int8(c))] x) (CMPBconst (ANDLconst [c] x) [0]) -> (TESTBconst [int64(int8(c))] x)
// Convert TESTx to TESTxconst if possible.
(TESTQ (MOVQconst [c]) x) && c < 1<<31 -> (TESTQconst [c] x)
(TESTL (MOVLconst [c]) x) -> (TESTLconst [c] x)
(TESTW (MOVLconst [c]) x) -> (TESTWconst [c] x)
(TESTB (MOVLconst [c]) x) -> (TESTBconst [c] x)
(TESTQ x (MOVQconst [c])) && c < 1<<31 -> (TESTQconst [c] x)
(TESTL x (MOVLconst [c])) -> (TESTLconst [c] x)
(TESTW x (MOVLconst [c])) -> (TESTWconst [c] x)
(TESTB x (MOVLconst [c])) -> (TESTBconst [c] x)
// TEST %reg,%reg is shorter than CMP // TEST %reg,%reg is shorter than CMP
(CMPQconst x [0]) -> (TESTQ x x) (CMPQconst x [0]) -> (TESTQ x x)
(CMPLconst x [0]) -> (TESTL x x) (CMPLconst x [0]) -> (TESTL x x)
......
...@@ -250,6 +250,11 @@ func init() { ...@@ -250,6 +250,11 @@ func init() {
{name: "UCOMISS", argLength: 2, reg: fp2flags, asm: "UCOMISS", typ: "Flags"}, // arg0 compare to arg1, f32 {name: "UCOMISS", argLength: 2, reg: fp2flags, asm: "UCOMISS", typ: "Flags"}, // arg0 compare to arg1, f32
{name: "UCOMISD", argLength: 2, reg: fp2flags, asm: "UCOMISD", typ: "Flags"}, // arg0 compare to arg1, f64 {name: "UCOMISD", argLength: 2, reg: fp2flags, asm: "UCOMISD", typ: "Flags"}, // arg0 compare to arg1, f64
{name: "BTL", argLength: 2, reg: gp2flags, asm: "BTL", typ: "Flags"}, // test whether bit arg0 % 32 in arg1 is set
{name: "BTQ", argLength: 2, reg: gp2flags, asm: "BTQ", typ: "Flags"}, // test whether bit arg0 % 64 in arg1 is set
{name: "BTLconst", argLength: 1, reg: gp1flags, asm: "BTL", typ: "Flags", aux: "Int8"}, // test whether bit auxint in arg0 is set, 0 <= auxint < 32
{name: "BTQconst", argLength: 1, reg: gp1flags, asm: "BTQ", typ: "Flags", aux: "Int8"}, // test whether bit auxint in arg0 is set, 0 <= auxint < 64
{name: "TESTQ", argLength: 2, reg: gp2flags, asm: "TESTQ", typ: "Flags"}, // (arg0 & arg1) compare to 0 {name: "TESTQ", argLength: 2, reg: gp2flags, asm: "TESTQ", typ: "Flags"}, // (arg0 & arg1) compare to 0
{name: "TESTL", argLength: 2, reg: gp2flags, asm: "TESTL", typ: "Flags"}, // (arg0 & arg1) compare to 0 {name: "TESTL", argLength: 2, reg: gp2flags, asm: "TESTL", typ: "Flags"}, // (arg0 & arg1) compare to 0
{name: "TESTW", argLength: 2, reg: gp2flags, asm: "TESTW", typ: "Flags"}, // (arg0 & arg1) compare to 0 {name: "TESTW", argLength: 2, reg: gp2flags, asm: "TESTW", typ: "Flags"}, // (arg0 & arg1) compare to 0
......
...@@ -491,6 +491,10 @@ const ( ...@@ -491,6 +491,10 @@ const (
OpAMD64CMPBconst OpAMD64CMPBconst
OpAMD64UCOMISS OpAMD64UCOMISS
OpAMD64UCOMISD OpAMD64UCOMISD
OpAMD64BTL
OpAMD64BTQ
OpAMD64BTLconst
OpAMD64BTQconst
OpAMD64TESTQ OpAMD64TESTQ
OpAMD64TESTL OpAMD64TESTL
OpAMD64TESTW OpAMD64TESTW
...@@ -5550,6 +5554,50 @@ var opcodeTable = [...]opInfo{ ...@@ -5550,6 +5554,50 @@ var opcodeTable = [...]opInfo{
}, },
}, },
}, },
{
name: "BTL",
argLen: 2,
asm: x86.ABTL,
reg: regInfo{
inputs: []inputInfo{
{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
{1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
},
},
{
name: "BTQ",
argLen: 2,
asm: x86.ABTQ,
reg: regInfo{
inputs: []inputInfo{
{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
{1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
},
},
{
name: "BTLconst",
auxType: auxInt8,
argLen: 1,
asm: x86.ABTL,
reg: regInfo{
inputs: []inputInfo{
{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
},
},
{
name: "BTQconst",
auxType: auxInt8,
argLen: 1,
asm: x86.ABTQ,
reg: regInfo{
inputs: []inputInfo{
{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
},
},
},
{ {
name: "TESTQ", name: "TESTQ",
argLen: 2, argLen: 2,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment