1
0
mirror of https://github.com/golang/go synced 2024-10-05 16:31:21 -06:00

[dev.ssa] cmd/compile/internal/ssa: fix shift operations

Convert shift ops to also encode the size of the shift amount.

Change signed right shift from using CMOV to using bit twiddles.
It is a little bit better (5 instructions instead of 4, but fewer
bytes and slightly faster code).  It's also a bit faster than
the 4-instruction branch version, even with a very predictable
branch.  As tested on my machine, YMMV.

Implement OCOM while we are here.

Change-Id: I8ca12dd62fae5d626dc0e6da5d4bbd34fd9640d2
Reviewed-on: https://go-review.googlesource.com/12867
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
This commit is contained in:
Keith Randall 2015-07-29 17:07:09 -07:00
parent bdb2d2810d
commit 4b803151ce
7 changed files with 2827 additions and 528 deletions

View File

@ -727,6 +727,15 @@ var opToSSA = map[opAndType]ssa.Op{
opAndType{OMINUS, TINT64}: ssa.OpNeg64,
opAndType{OMINUS, TUINT64}: ssa.OpNeg64,
opAndType{OCOM, TINT8}: ssa.OpCom8,
opAndType{OCOM, TUINT8}: ssa.OpCom8,
opAndType{OCOM, TINT16}: ssa.OpCom16,
opAndType{OCOM, TUINT16}: ssa.OpCom16,
opAndType{OCOM, TINT32}: ssa.OpCom32,
opAndType{OCOM, TUINT32}: ssa.OpCom32,
opAndType{OCOM, TINT64}: ssa.OpCom64,
opAndType{OCOM, TUINT64}: ssa.OpCom64,
opAndType{OMUL, TINT8}: ssa.OpMul8,
opAndType{OMUL, TUINT8}: ssa.OpMul8,
opAndType{OMUL, TINT16}: ssa.OpMul16,
@ -754,24 +763,6 @@ var opToSSA = map[opAndType]ssa.Op{
opAndType{OOR, TINT64}: ssa.OpOr64,
opAndType{OOR, TUINT64}: ssa.OpOr64,
opAndType{OLSH, TINT8}: ssa.OpLsh8,
opAndType{OLSH, TUINT8}: ssa.OpLsh8,
opAndType{OLSH, TINT16}: ssa.OpLsh16,
opAndType{OLSH, TUINT16}: ssa.OpLsh16,
opAndType{OLSH, TINT32}: ssa.OpLsh32,
opAndType{OLSH, TUINT32}: ssa.OpLsh32,
opAndType{OLSH, TINT64}: ssa.OpLsh64,
opAndType{OLSH, TUINT64}: ssa.OpLsh64,
opAndType{ORSH, TINT8}: ssa.OpRsh8,
opAndType{ORSH, TUINT8}: ssa.OpRsh8U,
opAndType{ORSH, TINT16}: ssa.OpRsh16,
opAndType{ORSH, TUINT16}: ssa.OpRsh16U,
opAndType{ORSH, TINT32}: ssa.OpRsh32,
opAndType{ORSH, TUINT32}: ssa.OpRsh32U,
opAndType{ORSH, TINT64}: ssa.OpRsh64,
opAndType{ORSH, TUINT64}: ssa.OpRsh64U,
opAndType{OEQ, TBOOL}: ssa.OpEq8,
opAndType{OEQ, TINT8}: ssa.OpEq8,
opAndType{OEQ, TUINT8}: ssa.OpEq8,
@ -877,6 +868,96 @@ func (s *state) ssaOp(op uint8, t *Type) ssa.Op {
return x
}
type opAndTwoTypes struct {
op uint8
etype1 uint8
etype2 uint8
}
var shiftOpToSSA = map[opAndTwoTypes]ssa.Op{
opAndTwoTypes{OLSH, TINT8, TUINT8}: ssa.OpLsh8x8,
opAndTwoTypes{OLSH, TUINT8, TUINT8}: ssa.OpLsh8x8,
opAndTwoTypes{OLSH, TINT8, TUINT16}: ssa.OpLsh8x16,
opAndTwoTypes{OLSH, TUINT8, TUINT16}: ssa.OpLsh8x16,
opAndTwoTypes{OLSH, TINT8, TUINT32}: ssa.OpLsh8x32,
opAndTwoTypes{OLSH, TUINT8, TUINT32}: ssa.OpLsh8x32,
opAndTwoTypes{OLSH, TINT8, TUINT64}: ssa.OpLsh8x64,
opAndTwoTypes{OLSH, TUINT8, TUINT64}: ssa.OpLsh8x64,
opAndTwoTypes{OLSH, TINT16, TUINT8}: ssa.OpLsh16x8,
opAndTwoTypes{OLSH, TUINT16, TUINT8}: ssa.OpLsh16x8,
opAndTwoTypes{OLSH, TINT16, TUINT16}: ssa.OpLsh16x16,
opAndTwoTypes{OLSH, TUINT16, TUINT16}: ssa.OpLsh16x16,
opAndTwoTypes{OLSH, TINT16, TUINT32}: ssa.OpLsh16x32,
opAndTwoTypes{OLSH, TUINT16, TUINT32}: ssa.OpLsh16x32,
opAndTwoTypes{OLSH, TINT16, TUINT64}: ssa.OpLsh16x64,
opAndTwoTypes{OLSH, TUINT16, TUINT64}: ssa.OpLsh16x64,
opAndTwoTypes{OLSH, TINT32, TUINT8}: ssa.OpLsh32x8,
opAndTwoTypes{OLSH, TUINT32, TUINT8}: ssa.OpLsh32x8,
opAndTwoTypes{OLSH, TINT32, TUINT16}: ssa.OpLsh32x16,
opAndTwoTypes{OLSH, TUINT32, TUINT16}: ssa.OpLsh32x16,
opAndTwoTypes{OLSH, TINT32, TUINT32}: ssa.OpLsh32x32,
opAndTwoTypes{OLSH, TUINT32, TUINT32}: ssa.OpLsh32x32,
opAndTwoTypes{OLSH, TINT32, TUINT64}: ssa.OpLsh32x64,
opAndTwoTypes{OLSH, TUINT32, TUINT64}: ssa.OpLsh32x64,
opAndTwoTypes{OLSH, TINT64, TUINT8}: ssa.OpLsh64x8,
opAndTwoTypes{OLSH, TUINT64, TUINT8}: ssa.OpLsh64x8,
opAndTwoTypes{OLSH, TINT64, TUINT16}: ssa.OpLsh64x16,
opAndTwoTypes{OLSH, TUINT64, TUINT16}: ssa.OpLsh64x16,
opAndTwoTypes{OLSH, TINT64, TUINT32}: ssa.OpLsh64x32,
opAndTwoTypes{OLSH, TUINT64, TUINT32}: ssa.OpLsh64x32,
opAndTwoTypes{OLSH, TINT64, TUINT64}: ssa.OpLsh64x64,
opAndTwoTypes{OLSH, TUINT64, TUINT64}: ssa.OpLsh64x64,
opAndTwoTypes{ORSH, TINT8, TUINT8}: ssa.OpRsh8x8,
opAndTwoTypes{ORSH, TUINT8, TUINT8}: ssa.OpRsh8Ux8,
opAndTwoTypes{ORSH, TINT8, TUINT16}: ssa.OpRsh8x16,
opAndTwoTypes{ORSH, TUINT8, TUINT16}: ssa.OpRsh8Ux16,
opAndTwoTypes{ORSH, TINT8, TUINT32}: ssa.OpRsh8x32,
opAndTwoTypes{ORSH, TUINT8, TUINT32}: ssa.OpRsh8Ux32,
opAndTwoTypes{ORSH, TINT8, TUINT64}: ssa.OpRsh8x64,
opAndTwoTypes{ORSH, TUINT8, TUINT64}: ssa.OpRsh8Ux64,
opAndTwoTypes{ORSH, TINT16, TUINT8}: ssa.OpRsh16x8,
opAndTwoTypes{ORSH, TUINT16, TUINT8}: ssa.OpRsh16Ux8,
opAndTwoTypes{ORSH, TINT16, TUINT16}: ssa.OpRsh16x16,
opAndTwoTypes{ORSH, TUINT16, TUINT16}: ssa.OpRsh16Ux16,
opAndTwoTypes{ORSH, TINT16, TUINT32}: ssa.OpRsh16x32,
opAndTwoTypes{ORSH, TUINT16, TUINT32}: ssa.OpRsh16Ux32,
opAndTwoTypes{ORSH, TINT16, TUINT64}: ssa.OpRsh16x64,
opAndTwoTypes{ORSH, TUINT16, TUINT64}: ssa.OpRsh16Ux64,
opAndTwoTypes{ORSH, TINT32, TUINT8}: ssa.OpRsh32x8,
opAndTwoTypes{ORSH, TUINT32, TUINT8}: ssa.OpRsh32Ux8,
opAndTwoTypes{ORSH, TINT32, TUINT16}: ssa.OpRsh32x16,
opAndTwoTypes{ORSH, TUINT32, TUINT16}: ssa.OpRsh32Ux16,
opAndTwoTypes{ORSH, TINT32, TUINT32}: ssa.OpRsh32x32,
opAndTwoTypes{ORSH, TUINT32, TUINT32}: ssa.OpRsh32Ux32,
opAndTwoTypes{ORSH, TINT32, TUINT64}: ssa.OpRsh32x64,
opAndTwoTypes{ORSH, TUINT32, TUINT64}: ssa.OpRsh32Ux64,
opAndTwoTypes{ORSH, TINT64, TUINT8}: ssa.OpRsh64x8,
opAndTwoTypes{ORSH, TUINT64, TUINT8}: ssa.OpRsh64Ux8,
opAndTwoTypes{ORSH, TINT64, TUINT16}: ssa.OpRsh64x16,
opAndTwoTypes{ORSH, TUINT64, TUINT16}: ssa.OpRsh64Ux16,
opAndTwoTypes{ORSH, TINT64, TUINT32}: ssa.OpRsh64x32,
opAndTwoTypes{ORSH, TUINT64, TUINT32}: ssa.OpRsh64Ux32,
opAndTwoTypes{ORSH, TINT64, TUINT64}: ssa.OpRsh64x64,
opAndTwoTypes{ORSH, TUINT64, TUINT64}: ssa.OpRsh64Ux64,
}
func (s *state) ssaShiftOp(op uint8, t *Type, u *Type) ssa.Op {
etype1 := s.concreteEtype(t)
etype2 := s.concreteEtype(u)
x, ok := shiftOpToSSA[opAndTwoTypes{op, etype1, etype2}]
if !ok {
s.Unimplementedf("unhandled shift op %s etype=%s/%s", opnames[op], Econv(int(etype1), 0), Econv(int(etype2), 0))
}
return x
}
// expr converts the expression n to ssa, adds it to s and returns the ssa result.
func (s *state) expr(n *Node) *ssa.Value {
s.pushLine(n.Lineno)
@ -999,10 +1080,14 @@ func (s *state) expr(n *Node) *ssa.Value {
a := s.expr(n.Left)
b := s.expr(n.Right)
return s.newValue2(s.ssaOp(n.Op, n.Left.Type), ssa.TypeBool, a, b)
case OADD, OAND, OLSH, OMUL, OOR, ORSH, OSUB:
case OADD, OSUB, OMUL, OAND, OOR:
a := s.expr(n.Left)
b := s.expr(n.Right)
return s.newValue2(s.ssaOp(n.Op, n.Type), a.Type, a, b)
case OLSH, ORSH:
a := s.expr(n.Left)
b := s.expr(n.Right)
return s.newValue2(s.ssaShiftOp(n.Op, n.Type, n.Right.Type), a.Type, a, b)
case OANDAND, OOROR:
// To implement OANDAND (and OOROR), we introduce a
// new temporary variable to hold the result. The
@ -1045,7 +1130,7 @@ func (s *state) expr(n *Node) *ssa.Value {
return s.variable(n, n.Type)
// unary ops
case ONOT, OMINUS:
case ONOT, OMINUS, OCOM:
a := s.expr(n.Left)
return s.newValue1(s.ssaOp(n.Op, n.Type), a.Type, a)
@ -1766,36 +1851,13 @@ func genValue(v *ssa.Value) {
p.From.Offset = v.AuxInt
p.To.Type = obj.TYPE_REG
p.To.Reg = r
case ssa.OpAMD64SBBQcarrymask:
case ssa.OpAMD64SBBQcarrymask, ssa.OpAMD64SBBLcarrymask:
r := regnum(v)
p := Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = r
p.To.Type = obj.TYPE_REG
p.To.Reg = r
case ssa.OpAMD64CMOVQCC:
r := regnum(v)
x := regnum(v.Args[1])
y := regnum(v.Args[2])
if x != r && y != r {
p := Prog(x86.AMOVQ)
p.From.Type = obj.TYPE_REG
p.From.Reg = x
p.To.Type = obj.TYPE_REG
p.To.Reg = r
x = r
}
var p *obj.Prog
if x == r {
p = Prog(x86.ACMOVQCS)
p.From.Reg = y
} else {
p = Prog(x86.ACMOVQCC)
p.From.Reg = x
}
p.From.Type = obj.TYPE_REG
p.To.Type = obj.TYPE_REG
p.To.Reg = r
case ssa.OpAMD64LEAQ1, ssa.OpAMD64LEAQ2, ssa.OpAMD64LEAQ4, ssa.OpAMD64LEAQ8:
p := Prog(x86.ALEAQ)
p.From.Type = obj.TYPE_MEM
@ -1967,7 +2029,8 @@ func genValue(v *ssa.Value) {
p := Prog(obj.ACALL)
p.To.Type = obj.TYPE_REG
p.To.Reg = regnum(v.Args[0])
case ssa.OpAMD64NEGQ, ssa.OpAMD64NEGL, ssa.OpAMD64NEGW, ssa.OpAMD64NEGB:
case ssa.OpAMD64NEGQ, ssa.OpAMD64NEGL, ssa.OpAMD64NEGW, ssa.OpAMD64NEGB,
ssa.OpAMD64NOTQ, ssa.OpAMD64NOTL, ssa.OpAMD64NOTW, ssa.OpAMD64NOTB:
p := Prog(v.Op.Asm())
p.To.Type = obj.TYPE_REG
p.To.Reg = regnum(v.Args[0])

View File

@ -50,6 +50,11 @@
(Neg16 x) -> (NEGW x)
(Neg8 x) -> (NEGB x)
(Com64 x) -> (NOTQ x)
(Com32 x) -> (NOTL x)
(Com16 x) -> (NOTW x)
(Com8 x) -> (NOTB x)
// Note: we always extend to 64 bits even though some ops don't need that many result bits.
(SignExt8to16 x) -> (MOVBQSX x)
(SignExt8to32 x) -> (MOVBQSX x)
@ -78,57 +83,71 @@
// TODO: other ConvNops are safe? Maybe all of them?
// Lowering shifts
// Note: unsigned shifts need to return 0 if shift amount is >= 64.
// mask = shift >= 64 ? 0 : 0xffffffffffffffff
// result = mask & arg << shift
// TODO: define ops per right-hand side size, like Lsh64x32 for int64(x)<<uint32(y)?
(Lsh64 <t> x y) && y.Type.Size() == 8 ->
(ANDQ (SHLQ <t> x y) (SBBQcarrymask <t> (CMPQconst <TypeFlags> [64] y)))
(Lsh64 <t> x y) && y.Type.Size() == 4 ->
(ANDQ (SHLQ <t> x y) (SBBQcarrymask <t> (CMPLconst <TypeFlags> [64] y)))
(Lsh64 <t> x y) && y.Type.Size() == 2 ->
(ANDQ (SHLQ <t> x y) (SBBQcarrymask <t> (CMPWconst <TypeFlags> [64] y)))
(Lsh64 <t> x y) && y.Type.Size() == 1 ->
(ANDQ (SHLQ <t> x y) (SBBQcarrymask <t> (CMPBconst <TypeFlags> [64] y)))
// Unsigned shifts need to return 0 if shift amount is >= width of shifted value.
// result = (arg << shift) & (shift >= argbits ? 0 : 0xffffffffffffffff)
// Note: for small shifts we generate 32 bits of mask even when we don't need it all.
(Lsh64x64 <t> x y) -> (ANDQ (SHLQ <t> x y) (SBBQcarrymask <t> (CMPQconst <TypeFlags> [64] y)))
(Lsh64x32 <t> x y) -> (ANDQ (SHLQ <t> x y) (SBBQcarrymask <t> (CMPLconst <TypeFlags> [64] y)))
(Lsh64x16 <t> x y) -> (ANDQ (SHLQ <t> x y) (SBBQcarrymask <t> (CMPWconst <TypeFlags> [64] y)))
(Lsh64x8 <t> x y) -> (ANDQ (SHLQ <t> x y) (SBBQcarrymask <t> (CMPBconst <TypeFlags> [64] y)))
(Lsh32 <t> x y) && y.Type.Size() == 8 ->
(ANDL (SHLL <t> x y) (SBBQcarrymask <t> (CMPQconst <TypeFlags> [32] y)))
(Lsh32 <t> x y) && y.Type.Size() == 4 ->
(ANDL (SHLL <t> x y) (SBBQcarrymask <t> (CMPLconst <TypeFlags> [32] y)))
(Lsh32 <t> x y) && y.Type.Size() == 2 ->
(ANDL (SHLL <t> x y) (SBBQcarrymask <t> (CMPWconst <TypeFlags> [32] y)))
(Lsh32 <t> x y) && y.Type.Size() == 1 ->
(ANDL (SHLL <t> x y) (SBBQcarrymask <t> (CMPBconst <TypeFlags> [32] y)))
(Lsh32x64 <t> x y) -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPQconst <TypeFlags> [32] y)))
(Lsh32x32 <t> x y) -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPLconst <TypeFlags> [32] y)))
(Lsh32x16 <t> x y) -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPWconst <TypeFlags> [32] y)))
(Lsh32x8 <t> x y) -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPBconst <TypeFlags> [32] y)))
(Lsh16 <t> x y) && y.Type.Size() == 8 ->
(ANDW (SHLW <t> x y) (SBBQcarrymask <t> (CMPQconst <TypeFlags> [16] y)))
(Lsh16 <t> x y) && y.Type.Size() == 4 ->
(ANDW (SHLW <t> x y) (SBBQcarrymask <t> (CMPLconst <TypeFlags> [16] y)))
(Lsh16 <t> x y) && y.Type.Size() == 2 ->
(ANDW (SHLW <t> x y) (SBBQcarrymask <t> (CMPWconst <TypeFlags> [16] y)))
(Lsh16 <t> x y) && y.Type.Size() == 1 ->
(ANDW (SHLW <t> x y) (SBBQcarrymask <t> (CMPBconst <TypeFlags> [16] y)))
(Lsh16x64 <t> x y) -> (ANDW (SHLW <t> x y) (SBBLcarrymask <t> (CMPQconst <TypeFlags> [16] y)))
(Lsh16x32 <t> x y) -> (ANDW (SHLW <t> x y) (SBBLcarrymask <t> (CMPLconst <TypeFlags> [16] y)))
(Lsh16x16 <t> x y) -> (ANDW (SHLW <t> x y) (SBBLcarrymask <t> (CMPWconst <TypeFlags> [16] y)))
(Lsh16x8 <t> x y) -> (ANDW (SHLW <t> x y) (SBBLcarrymask <t> (CMPBconst <TypeFlags> [16] y)))
(Lsh8 <t> x y) && y.Type.Size() == 8 ->
(ANDB (SHLB <t> x y) (SBBQcarrymask <t> (CMPQconst <TypeFlags> [8] y)))
(Lsh8 <t> x y) && y.Type.Size() == 4 ->
(ANDB (SHLB <t> x y) (SBBQcarrymask <t> (CMPLconst <TypeFlags> [8] y)))
(Lsh8 <t> x y) && y.Type.Size() == 2 ->
(ANDB (SHLB <t> x y) (SBBQcarrymask <t> (CMPWconst <TypeFlags> [8] y)))
(Lsh8 <t> x y) && y.Type.Size() == 1 ->
(ANDB (SHLB <t> x y) (SBBQcarrymask <t> (CMPBconst <TypeFlags> [8] y)))
(Lsh8x64 <t> x y) -> (ANDB (SHLB <t> x y) (SBBLcarrymask <t> (CMPQconst <TypeFlags> [8] y)))
(Lsh8x32 <t> x y) -> (ANDB (SHLB <t> x y) (SBBLcarrymask <t> (CMPLconst <TypeFlags> [8] y)))
(Lsh8x16 <t> x y) -> (ANDB (SHLB <t> x y) (SBBLcarrymask <t> (CMPWconst <TypeFlags> [8] y)))
(Lsh8x8 <t> x y) -> (ANDB (SHLB <t> x y) (SBBLcarrymask <t> (CMPBconst <TypeFlags> [8] y)))
(Rsh64U <t> x y) && y.Type.Size() == 8 ->
(ANDQ (SHRQ <t> x y) (SBBQcarrymask <t> (CMPQconst <TypeFlags> [64] y)))
(Rsh64Ux64 <t> x y) -> (ANDQ (SHRQ <t> x y) (SBBQcarrymask <t> (CMPQconst <TypeFlags> [64] y)))
(Rsh64Ux32 <t> x y) -> (ANDQ (SHRQ <t> x y) (SBBQcarrymask <t> (CMPLconst <TypeFlags> [64] y)))
(Rsh64Ux16 <t> x y) -> (ANDQ (SHRQ <t> x y) (SBBQcarrymask <t> (CMPWconst <TypeFlags> [64] y)))
(Rsh64Ux8 <t> x y) -> (ANDQ (SHRQ <t> x y) (SBBQcarrymask <t> (CMPBconst <TypeFlags> [64] y)))
// Note: signed right shift needs to return 0/-1 if shift amount is >= 64.
// if shift > 63 { shift = 63 }
// result = arg >> shift
(Rsh64 <t> x y) && y.Type.Size() == 8 ->
(SARQ <t> x (CMOVQCC <t>
(CMPQconst <TypeFlags> [64] y)
(MOVQconst <t> [63])
y))
(Rsh32Ux64 <t> x y) -> (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMPQconst <TypeFlags> [32] y)))
(Rsh32Ux32 <t> x y) -> (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMPLconst <TypeFlags> [32] y)))
(Rsh32Ux16 <t> x y) -> (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMPWconst <TypeFlags> [32] y)))
(Rsh32Ux8 <t> x y) -> (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMPBconst <TypeFlags> [32] y)))
(Rsh16Ux64 <t> x y) -> (ANDW (SHRW <t> x y) (SBBLcarrymask <t> (CMPQconst <TypeFlags> [16] y)))
(Rsh16Ux32 <t> x y) -> (ANDW (SHRW <t> x y) (SBBLcarrymask <t> (CMPLconst <TypeFlags> [16] y)))
(Rsh16Ux16 <t> x y) -> (ANDW (SHRW <t> x y) (SBBLcarrymask <t> (CMPWconst <TypeFlags> [16] y)))
(Rsh16Ux8 <t> x y) -> (ANDW (SHRW <t> x y) (SBBLcarrymask <t> (CMPBconst <TypeFlags> [16] y)))
(Rsh8Ux64 <t> x y) -> (ANDB (SHRB <t> x y) (SBBLcarrymask <t> (CMPQconst <TypeFlags> [8] y)))
(Rsh8Ux32 <t> x y) -> (ANDB (SHRB <t> x y) (SBBLcarrymask <t> (CMPLconst <TypeFlags> [8] y)))
(Rsh8Ux16 <t> x y) -> (ANDB (SHRB <t> x y) (SBBLcarrymask <t> (CMPWconst <TypeFlags> [8] y)))
(Rsh8Ux8 <t> x y) -> (ANDB (SHRB <t> x y) (SBBLcarrymask <t> (CMPBconst <TypeFlags> [8] y)))
// Signed right shift needs to return 0/-1 if shift amount is >= width of shifted value.
// We implement this by setting the shift value to -1 (all ones) if the shift value is >= width.
// Note: for small shift widths we generate 32 bits of mask even when we don't need it all.
(Rsh64x64 <t> x y) -> (SARQ <t> x (ORQ <y.Type> y (NOTQ <y.Type> (SBBQcarrymask <y.Type> (CMPQconst <TypeFlags> [64] y)))))
(Rsh64x32 <t> x y) -> (SARQ <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst <TypeFlags> [64] y)))))
(Rsh64x16 <t> x y) -> (SARQ <t> x (ORW <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst <TypeFlags> [64] y)))))
(Rsh64x8 <t> x y) -> (SARQ <t> x (ORB <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst <TypeFlags> [64] y)))))
(Rsh32x64 <t> x y) -> (SARL <t> x (ORQ <y.Type> y (NOTQ <y.Type> (SBBQcarrymask <y.Type> (CMPQconst <TypeFlags> [32] y)))))
(Rsh32x32 <t> x y) -> (SARL <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst <TypeFlags> [32] y)))))
(Rsh32x16 <t> x y) -> (SARL <t> x (ORW <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst <TypeFlags> [32] y)))))
(Rsh32x8 <t> x y) -> (SARL <t> x (ORB <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst <TypeFlags> [32] y)))))
(Rsh16x64 <t> x y) -> (SARW <t> x (ORQ <y.Type> y (NOTQ <y.Type> (SBBQcarrymask <y.Type> (CMPQconst <TypeFlags> [16] y)))))
(Rsh16x32 <t> x y) -> (SARW <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst <TypeFlags> [16] y)))))
(Rsh16x16 <t> x y) -> (SARW <t> x (ORW <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst <TypeFlags> [16] y)))))
(Rsh16x8 <t> x y) -> (SARW <t> x (ORB <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst <TypeFlags> [16] y)))))
(Rsh8x64 <t> x y) -> (SARB <t> x (ORQ <y.Type> y (NOTQ <y.Type> (SBBQcarrymask <y.Type> (CMPQconst <TypeFlags> [8] y)))))
(Rsh8x32 <t> x y) -> (SARB <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst <TypeFlags> [8] y)))))
(Rsh8x16 <t> x y) -> (SARB <t> x (ORW <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst <TypeFlags> [8] y)))))
(Rsh8x8 <t> x y) -> (SARB <t> x (ORB <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst <TypeFlags> [8] y)))))
(Less64 x y) -> (SETL (CMPQ <TypeFlags> x y))
(Less32 x y) -> (SETL (CMPL <TypeFlags> x y))
@ -398,10 +417,58 @@
(EQ (InvertFlags cmp) yes no) -> (EQ cmp yes no)
(NE (InvertFlags cmp) yes no) -> (NE cmp yes no)
// get rid of >=64 code for constant shifts
// get rid of overflow code for constant shifts
(SBBQcarrymask (CMPQconst [c] (MOVQconst [d]))) && inBounds(d, c) -> (MOVQconst [-1])
(SBBQcarrymask (CMPQconst [c] (MOVQconst [d]))) && !inBounds(d, c) -> (MOVQconst [0])
(ANDQconst [0] _) -> (MOVQconst [0])
(ANDQconst [-1] x) -> (Copy x)
(CMOVQCC (CMPQconst [c] (MOVQconst [d])) _ x) && inBounds(d, c) -> (Copy x)
(CMOVQCC (CMPQconst [c] (MOVQconst [d])) x _) && !inBounds(d, c) -> (Copy x)
(SBBQcarrymask (CMPLconst [c] (MOVLconst [d]))) && inBounds(int64(int32(d)), int64(int32(c))) -> (MOVQconst [-1])
(SBBQcarrymask (CMPLconst [c] (MOVLconst [d]))) && !inBounds(int64(int32(d)), int64(int32(c))) -> (MOVQconst [0])
(SBBQcarrymask (CMPWconst [c] (MOVWconst [d]))) && inBounds(int64(int16(d)), int64(int16(c))) -> (MOVQconst [-1])
(SBBQcarrymask (CMPWconst [c] (MOVWconst [d]))) && !inBounds(int64(int16(d)), int64(int16(c))) -> (MOVQconst [0])
(SBBQcarrymask (CMPBconst [c] (MOVBconst [d]))) && inBounds(int64(int8(d)), int64(int8(c))) -> (MOVQconst [-1])
(SBBQcarrymask (CMPBconst [c] (MOVBconst [d]))) && !inBounds(int64(int8(d)), int64(int8(c))) -> (MOVQconst [0])
(ANDQconst [0] _) -> (MOVQconst [0])
(ANDLconst [c] _) && int32(c)==0 -> (MOVLconst [0])
(ANDWconst [c] _) && int16(c)==0 -> (MOVWconst [0])
(ANDBconst [c] _) && int8(c)==0 -> (MOVBconst [0])
(ANDQconst [-1] x) -> (Copy x)
(ANDLconst [c] x) && int32(c)==-1 -> (Copy x)
(ANDWconst [c] x) && int16(c)==-1 -> (Copy x)
(ANDBconst [c] x) && int8(c)==-1 -> (Copy x)
(ORQconst [0] x) -> (Copy x)
(ORLconst [c] x) && int32(c)==0 -> (Copy x)
(ORWconst [c] x) && int16(c)==0 -> (Copy x)
(ORBconst [c] x) && int8(c)==0 -> (Copy x)
(ORQconst [-1] _) -> (MOVQconst [-1])
(ORLconst [c] _) && int32(c)==-1 -> (MOVLconst [-1])
(ORWconst [c] _) && int16(c)==-1 -> (MOVWconst [-1])
(ORBconst [c] _) && int8(c)==-1 -> (MOVBconst [-1])
// generic constant folding
// TODO: more of this
(ADDQconst [c] (MOVQconst [d])) -> (MOVQconst [c+d])
(ADDLconst [c] (MOVLconst [d])) -> (MOVLconst [c+d])
(ADDWconst [c] (MOVWconst [d])) -> (MOVWconst [c+d])
(ADDBconst [c] (MOVBconst [d])) -> (MOVBconst [c+d])
(SUBQconst [c] (MOVQconst [d])) -> (MOVQconst [c-d])
(SUBLconst [c] (MOVLconst [d])) -> (MOVLconst [c-d])
(SUBWconst [c] (MOVWconst [d])) -> (MOVWconst [c-d])
(SUBBconst [c] (MOVBconst [d])) -> (MOVBconst [c-d])
(MULQconst [c] (MOVQconst [d])) -> (MOVQconst [c*d])
(MULLconst [c] (MOVLconst [d])) -> (MOVLconst [c*d])
(MULWconst [c] (MOVWconst [d])) -> (MOVWconst [c*d])
(ANDQconst [c] (MOVQconst [d])) -> (MOVQconst [c&d])
(ANDLconst [c] (MOVLconst [d])) -> (MOVLconst [c&d])
(ANDWconst [c] (MOVWconst [d])) -> (MOVWconst [c&d])
(ANDBconst [c] (MOVBconst [d])) -> (MOVBconst [c&d])
(ORQconst [c] (MOVQconst [d])) -> (MOVQconst [c|d])
(ORLconst [c] (MOVLconst [d])) -> (MOVLconst [c|d])
(ORWconst [c] (MOVWconst [d])) -> (MOVWconst [c|d])
(ORBconst [c] (MOVBconst [d])) -> (MOVBconst [c|d])
(XORQconst [c] (MOVQconst [d])) -> (MOVQconst [c^d])
(XORLconst [c] (MOVLconst [d])) -> (MOVLconst [c^d])
(XORWconst [c] (MOVWconst [d])) -> (MOVWconst [c^d])
(XORBconst [c] (MOVBconst [d])) -> (MOVBconst [c^d])
(NOTQ (MOVQconst [c])) -> (MOVQconst [^c])
(NOTL (MOVLconst [c])) -> (MOVLconst [^c])
(NOTW (MOVWconst [c])) -> (MOVWconst [^c])
(NOTB (MOVBconst [c])) -> (MOVBconst [^c])

View File

@ -89,7 +89,6 @@ func init() {
gpstoreconst := regInfo{[]regMask{gpspsb, 0}, 0, nil}
gpstoreidx := regInfo{[]regMask{gpspsb, gpsp, gpsp, 0}, 0, nil}
flagsgp := regInfo{[]regMask{flags}, 0, []regMask{gp}}
cmov := regInfo{[]regMask{flags, gp, gp}, 0, []regMask{gp}}
// Suffixes encode the bit width of various instructions.
// Q = 64 bit, L = 32 bit, W = 16 bit, B = 8 bit
@ -201,7 +200,14 @@ func init() {
{name: "NEGW", reg: gp11, asm: "NEGW"}, // -arg0
{name: "NEGB", reg: gp11, asm: "NEGB"}, // -arg0
{name: "NOTQ", reg: gp11, asm: "NOTQ"}, // ^arg0
{name: "NOTL", reg: gp11, asm: "NOTL"}, // ^arg0
{name: "NOTW", reg: gp11, asm: "NOTW"}, // ^arg0
{name: "NOTB", reg: gp11, asm: "NOTB"}, // ^arg0
{name: "SBBQcarrymask", reg: flagsgp1, asm: "SBBQ"}, // (int64)(-1) if carry is set, 0 if carry is clear.
{name: "SBBLcarrymask", reg: flagsgp1, asm: "SBBL"}, // (int32)(-1) if carry is set, 0 if carry is clear.
// Note: SBBW and SBBB are subsumed by SBBL
{name: "SETEQ", reg: flagsgp, asm: "SETEQ"}, // extract == condition from arg0
{name: "SETNE", reg: flagsgp, asm: "SETNE"}, // extract != condition from arg0
@ -214,8 +220,6 @@ func init() {
{name: "SETA", reg: flagsgp, asm: "SETHI"}, // extract unsigned > condition from arg0
{name: "SETAE", reg: flagsgp, asm: "SETCC"}, // extract unsigned >= condition from arg0
{name: "CMOVQCC", reg: cmov}, // carry clear
{name: "MOVBQSX", reg: gp11, asm: "MOVBQSX"}, // sign extend arg0 from int8 to int64
{name: "MOVBQZX", reg: gp11, asm: "MOVBQZX"}, // zero extend arg0 from int8 to int64
{name: "MOVWQSX", reg: gp11, asm: "MOVWQSX"}, // sign extend arg0 from int16 to int64

View File

@ -42,19 +42,57 @@ var genericOps = []opData{
{name: "Xor32"},
{name: "Xor64"},
{name: "Lsh8"}, // arg0 << arg1
{name: "Lsh16"},
{name: "Lsh32"},
{name: "Lsh64"},
// For shifts, AxB means the shifted value has A bits and the shift amount has B bits.
{name: "Lsh8x8"}, // arg0 << arg1
{name: "Lsh8x16"},
{name: "Lsh8x32"},
{name: "Lsh8x64"},
{name: "Lsh16x8"},
{name: "Lsh16x16"},
{name: "Lsh16x32"},
{name: "Lsh16x64"},
{name: "Lsh32x8"},
{name: "Lsh32x16"},
{name: "Lsh32x32"},
{name: "Lsh32x64"},
{name: "Lsh64x8"},
{name: "Lsh64x16"},
{name: "Lsh64x32"},
{name: "Lsh64x64"},
{name: "Rsh8"}, // arg0 >> arg1
{name: "Rsh8U"},
{name: "Rsh16"},
{name: "Rsh16U"},
{name: "Rsh32"},
{name: "Rsh32U"},
{name: "Rsh64"},
{name: "Rsh64U"},
{name: "Rsh8x8"}, // arg0 >> arg1, signed
{name: "Rsh8x16"},
{name: "Rsh8x32"},
{name: "Rsh8x64"},
{name: "Rsh16x8"},
{name: "Rsh16x16"},
{name: "Rsh16x32"},
{name: "Rsh16x64"},
{name: "Rsh32x8"},
{name: "Rsh32x16"},
{name: "Rsh32x32"},
{name: "Rsh32x64"},
{name: "Rsh64x8"},
{name: "Rsh64x16"},
{name: "Rsh64x32"},
{name: "Rsh64x64"},
{name: "Rsh8Ux8"}, // arg0 >> arg1, unsigned
{name: "Rsh8Ux16"},
{name: "Rsh8Ux32"},
{name: "Rsh8Ux64"},
{name: "Rsh16Ux8"},
{name: "Rsh16Ux16"},
{name: "Rsh16Ux32"},
{name: "Rsh16Ux64"},
{name: "Rsh32Ux8"},
{name: "Rsh32Ux16"},
{name: "Rsh32Ux32"},
{name: "Rsh32Ux64"},
{name: "Rsh64Ux8"},
{name: "Rsh64Ux16"},
{name: "Rsh64Ux32"},
{name: "Rsh64Ux64"},
// 2-input comparisons
{name: "Eq8"}, // arg0 == arg1
@ -110,11 +148,16 @@ var genericOps = []opData{
// 1-input ops
{name: "Not"}, // !arg0
{name: "Neg8"}, // - arg0
{name: "Neg8"}, // -arg0
{name: "Neg16"},
{name: "Neg32"},
{name: "Neg64"},
{name: "Com8"}, // ^arg0
{name: "Com16"},
{name: "Com32"},
{name: "Com64"},
// Data movement
{name: "Phi"}, // select an argument based on which predecessor block we came from
{name: "Copy"}, // output = arg0

View File

@ -141,7 +141,12 @@ const (
OpAMD64NEGL
OpAMD64NEGW
OpAMD64NEGB
OpAMD64NOTQ
OpAMD64NOTL
OpAMD64NOTW
OpAMD64NOTB
OpAMD64SBBQcarrymask
OpAMD64SBBLcarrymask
OpAMD64SETEQ
OpAMD64SETNE
OpAMD64SETL
@ -152,7 +157,6 @@ const (
OpAMD64SETBE
OpAMD64SETA
OpAMD64SETAE
OpAMD64CMOVQCC
OpAMD64MOVBQSX
OpAMD64MOVBQZX
OpAMD64MOVWQSX
@ -213,18 +217,54 @@ const (
OpXor16
OpXor32
OpXor64
OpLsh8
OpLsh16
OpLsh32
OpLsh64
OpRsh8
OpRsh8U
OpRsh16
OpRsh16U
OpRsh32
OpRsh32U
OpRsh64
OpRsh64U
OpLsh8x8
OpLsh8x16
OpLsh8x32
OpLsh8x64
OpLsh16x8
OpLsh16x16
OpLsh16x32
OpLsh16x64
OpLsh32x8
OpLsh32x16
OpLsh32x32
OpLsh32x64
OpLsh64x8
OpLsh64x16
OpLsh64x32
OpLsh64x64
OpRsh8x8
OpRsh8x16
OpRsh8x32
OpRsh8x64
OpRsh16x8
OpRsh16x16
OpRsh16x32
OpRsh16x64
OpRsh32x8
OpRsh32x16
OpRsh32x32
OpRsh32x64
OpRsh64x8
OpRsh64x16
OpRsh64x32
OpRsh64x64
OpRsh8Ux8
OpRsh8Ux16
OpRsh8Ux32
OpRsh8Ux64
OpRsh16Ux8
OpRsh16Ux16
OpRsh16Ux32
OpRsh16Ux64
OpRsh32Ux8
OpRsh32Ux16
OpRsh32Ux32
OpRsh32Ux64
OpRsh64Ux8
OpRsh64Ux16
OpRsh64Ux32
OpRsh64Ux64
OpEq8
OpEq16
OpEq32
@ -274,6 +314,10 @@ const (
OpNeg16
OpNeg32
OpNeg64
OpCom8
OpCom16
OpCom32
OpCom64
OpPhi
OpCopy
OpConstBool
@ -1458,6 +1502,54 @@ var opcodeTable = [...]opInfo{
},
},
},
{
name: "NOTQ",
asm: x86.ANOTQ,
reg: regInfo{
inputs: []regMask{
65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15
},
outputs: []regMask{
65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15
},
},
},
{
name: "NOTL",
asm: x86.ANOTL,
reg: regInfo{
inputs: []regMask{
65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15
},
outputs: []regMask{
65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15
},
},
},
{
name: "NOTW",
asm: x86.ANOTW,
reg: regInfo{
inputs: []regMask{
65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15
},
outputs: []regMask{
65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15
},
},
},
{
name: "NOTB",
asm: x86.ANOTB,
reg: regInfo{
inputs: []regMask{
65535, // .AX .CX .DX .BX .SP .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15
},
outputs: []regMask{
65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15
},
},
},
{
name: "SBBQcarrymask",
asm: x86.ASBBQ,
@ -1470,6 +1562,18 @@ var opcodeTable = [...]opInfo{
},
},
},
{
name: "SBBLcarrymask",
asm: x86.ASBBL,
reg: regInfo{
inputs: []regMask{
8589934592, // .FLAGS
},
outputs: []regMask{
65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15
},
},
},
{
name: "SETEQ",
asm: x86.ASETEQ,
@ -1590,19 +1694,6 @@ var opcodeTable = [...]opInfo{
},
},
},
{
name: "CMOVQCC",
reg: regInfo{
inputs: []regMask{
8589934592, // .FLAGS
65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15
65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15
},
outputs: []regMask{
65519, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15
},
},
},
{
name: "MOVBQSX",
asm: x86.AMOVBQSX,
@ -2072,51 +2163,195 @@ var opcodeTable = [...]opInfo{
generic: true,
},
{
name: "Lsh8",
name: "Lsh8x8",
generic: true,
},
{
name: "Lsh16",
name: "Lsh8x16",
generic: true,
},
{
name: "Lsh32",
name: "Lsh8x32",
generic: true,
},
{
name: "Lsh64",
name: "Lsh8x64",
generic: true,
},
{
name: "Rsh8",
name: "Lsh16x8",
generic: true,
},
{
name: "Rsh8U",
name: "Lsh16x16",
generic: true,
},
{
name: "Rsh16",
name: "Lsh16x32",
generic: true,
},
{
name: "Rsh16U",
name: "Lsh16x64",
generic: true,
},
{
name: "Rsh32",
name: "Lsh32x8",
generic: true,
},
{
name: "Rsh32U",
name: "Lsh32x16",
generic: true,
},
{
name: "Rsh64",
name: "Lsh32x32",
generic: true,
},
{
name: "Rsh64U",
name: "Lsh32x64",
generic: true,
},
{
name: "Lsh64x8",
generic: true,
},
{
name: "Lsh64x16",
generic: true,
},
{
name: "Lsh64x32",
generic: true,
},
{
name: "Lsh64x64",
generic: true,
},
{
name: "Rsh8x8",
generic: true,
},
{
name: "Rsh8x16",
generic: true,
},
{
name: "Rsh8x32",
generic: true,
},
{
name: "Rsh8x64",
generic: true,
},
{
name: "Rsh16x8",
generic: true,
},
{
name: "Rsh16x16",
generic: true,
},
{
name: "Rsh16x32",
generic: true,
},
{
name: "Rsh16x64",
generic: true,
},
{
name: "Rsh32x8",
generic: true,
},
{
name: "Rsh32x16",
generic: true,
},
{
name: "Rsh32x32",
generic: true,
},
{
name: "Rsh32x64",
generic: true,
},
{
name: "Rsh64x8",
generic: true,
},
{
name: "Rsh64x16",
generic: true,
},
{
name: "Rsh64x32",
generic: true,
},
{
name: "Rsh64x64",
generic: true,
},
{
name: "Rsh8Ux8",
generic: true,
},
{
name: "Rsh8Ux16",
generic: true,
},
{
name: "Rsh8Ux32",
generic: true,
},
{
name: "Rsh8Ux64",
generic: true,
},
{
name: "Rsh16Ux8",
generic: true,
},
{
name: "Rsh16Ux16",
generic: true,
},
{
name: "Rsh16Ux32",
generic: true,
},
{
name: "Rsh16Ux64",
generic: true,
},
{
name: "Rsh32Ux8",
generic: true,
},
{
name: "Rsh32Ux16",
generic: true,
},
{
name: "Rsh32Ux32",
generic: true,
},
{
name: "Rsh32Ux64",
generic: true,
},
{
name: "Rsh64Ux8",
generic: true,
},
{
name: "Rsh64Ux16",
generic: true,
},
{
name: "Rsh64Ux32",
generic: true,
},
{
name: "Rsh64Ux64",
generic: true,
},
{
@ -2315,6 +2550,22 @@ var opcodeTable = [...]opInfo{
name: "Neg64",
generic: true,
},
{
name: "Com8",
generic: true,
},
{
name: "Com16",
generic: true,
},
{
name: "Com32",
generic: true,
},
{
name: "Com64",
generic: true,
},
{
name: "Phi",
generic: true,

File diff suppressed because it is too large Load Diff

View File

@ -10,17 +10,17 @@ import (
func TestShiftConstAMD64(t *testing.T) {
c := NewConfig("amd64", DummyFrontend{t})
fun := makeConstShiftFunc(c, 18, OpLsh64, TypeUInt64)
fun := makeConstShiftFunc(c, 18, OpLsh64x64, TypeUInt64)
checkOpcodeCounts(t, fun.f, map[Op]int{OpAMD64SHLQconst: 1, OpAMD64CMPQconst: 0, OpAMD64ANDQconst: 0})
fun = makeConstShiftFunc(c, 66, OpLsh64, TypeUInt64)
fun = makeConstShiftFunc(c, 66, OpLsh64x64, TypeUInt64)
checkOpcodeCounts(t, fun.f, map[Op]int{OpAMD64SHLQconst: 0, OpAMD64CMPQconst: 0, OpAMD64ANDQconst: 0})
fun = makeConstShiftFunc(c, 18, OpRsh64U, TypeUInt64)
fun = makeConstShiftFunc(c, 18, OpRsh64Ux64, TypeUInt64)
checkOpcodeCounts(t, fun.f, map[Op]int{OpAMD64SHRQconst: 1, OpAMD64CMPQconst: 0, OpAMD64ANDQconst: 0})
fun = makeConstShiftFunc(c, 66, OpRsh64U, TypeUInt64)
fun = makeConstShiftFunc(c, 66, OpRsh64Ux64, TypeUInt64)
checkOpcodeCounts(t, fun.f, map[Op]int{OpAMD64SHRQconst: 0, OpAMD64CMPQconst: 0, OpAMD64ANDQconst: 0})
fun = makeConstShiftFunc(c, 18, OpRsh64, TypeInt64)
fun = makeConstShiftFunc(c, 18, OpRsh64x64, TypeInt64)
checkOpcodeCounts(t, fun.f, map[Op]int{OpAMD64SARQconst: 1, OpAMD64CMPQconst: 0})
fun = makeConstShiftFunc(c, 66, OpRsh64, TypeInt64)
fun = makeConstShiftFunc(c, 66, OpRsh64x64, TypeInt64)
checkOpcodeCounts(t, fun.f, map[Op]int{OpAMD64SARQconst: 1, OpAMD64CMPQconst: 0})
}