Commit 7987238d authored by Lynn Boger's avatar Lynn Boger

cmd/asm,cmd/compile: clean up isel codegen on ppc64x

This cleans up the isel code generation in ssa for ppc64x.

Current there is no isel op and the isel code is only
generated from pseudo ops in ppc64/ssa.go, and only using
operands with values 0 or 1. When the isel is generated,
there is always a load of 1 into the temp register before it.

This change implements the isel op so it can be used in PPC64.rules,
and can recognize operand values other than 0 or 1. This also
eliminates the forced load of 1, so it will be loaded only if
needed.

This will make the isel code generation consistent with other ops,
and allow future rule changes that can take advantage of having
a more general purpose isel rule.

Change-Id: I363e1dbd3f7f5dfecb53187ad51cce409a8d1f8d
Reviewed-on: https://go-review.googlesource.com/c/go/+/195057
Run-TryBot: Lynn Boger <laboger@linux.vnet.ibm.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarCarlos Eduardo Seo <cseo@linux.vnet.ibm.com>
parent eb6ce1cf
......@@ -47,7 +47,7 @@ func IsPPC64ISEL(op obj.As) bool {
// one of the CMP instructions that require special handling.
func IsPPC64CMP(op obj.As) bool {
switch op {
case ppc64.ACMP, ppc64.ACMPU, ppc64.ACMPW, ppc64.ACMPWU:
case ppc64.ACMP, ppc64.ACMPU, ppc64.ACMPW, ppc64.ACMPWU, ppc64.AFCMPU:
return true
}
return false
......
......@@ -15,28 +15,6 @@ import (
"strings"
)
// iselOp encodes mapping of comparison operations onto ISEL operands
type iselOp struct {
cond int64
valueIfCond int // if cond is true, the value to return (0 or 1)
}
// Input registers to ISEL used for comparison. Index 0 is zero, 1 is (will be) 1
var iselRegs = [2]int16{ppc64.REG_R0, ppc64.REGTMP}
var iselOps = map[ssa.Op]iselOp{
ssa.OpPPC64Equal: {cond: ppc64.C_COND_EQ, valueIfCond: 1},
ssa.OpPPC64NotEqual: {cond: ppc64.C_COND_EQ, valueIfCond: 0},
ssa.OpPPC64LessThan: {cond: ppc64.C_COND_LT, valueIfCond: 1},
ssa.OpPPC64GreaterEqual: {cond: ppc64.C_COND_LT, valueIfCond: 0},
ssa.OpPPC64GreaterThan: {cond: ppc64.C_COND_GT, valueIfCond: 1},
ssa.OpPPC64LessEqual: {cond: ppc64.C_COND_GT, valueIfCond: 0},
ssa.OpPPC64FLessThan: {cond: ppc64.C_COND_LT, valueIfCond: 1},
ssa.OpPPC64FGreaterThan: {cond: ppc64.C_COND_GT, valueIfCond: 1},
ssa.OpPPC64FLessEqual: {cond: ppc64.C_COND_LT, valueIfCond: 1}, // 2 comparisons, 2nd is EQ
ssa.OpPPC64FGreaterEqual: {cond: ppc64.C_COND_GT, valueIfCond: 1}, // 2 comparisons, 2nd is EQ
}
// markMoves marks any MOVXconst ops that need to avoid clobbering flags.
func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {
// flive := b.FlagsLiveAtEnd
......@@ -120,17 +98,6 @@ func storeByType(t *types.Type) obj.As {
panic("bad store type")
}
func ssaGenISEL(s *gc.SSAGenState, v *ssa.Value, cr int64, r1, r2 int16) {
r := v.Reg()
p := s.Prog(ppc64.AISEL)
p.To.Type = obj.TYPE_REG
p.To.Reg = r
p.Reg = r1
p.SetFrom3(obj.Addr{Type: obj.TYPE_REG, Reg: r2})
p.From.Type = obj.TYPE_CONST
p.From.Offset = cr
}
func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
switch v.Op {
case ssa.OpCopy:
......@@ -843,43 +810,32 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
p.To.Reg = v.Args[0].Reg()
gc.AddAux(&p.To, v)
case ssa.OpPPC64Equal,
ssa.OpPPC64NotEqual,
ssa.OpPPC64LessThan,
ssa.OpPPC64FLessThan,
ssa.OpPPC64LessEqual,
ssa.OpPPC64GreaterThan,
ssa.OpPPC64FGreaterThan,
ssa.OpPPC64GreaterEqual:
// On Power7 or later, can use isel instruction:
// for a < b, a > b, a = b:
// rtmp := 1
// isel rt,rtmp,r0,cond // rt is target in ppc asm
// for a >= b, a <= b, a != b:
// rtmp := 1
// isel rt,0,rtmp,!cond // rt is target in ppc asm
p := s.Prog(ppc64.AMOVD)
p.From.Type = obj.TYPE_CONST
p.From.Offset = 1
case ssa.OpPPC64ISEL, ssa.OpPPC64ISELB:
// ISEL, ISELB
// AuxInt value indicates condition: 0=LT 1=GT 2=EQ 4=GE 5=LE 6=NE
// ISEL only accepts 0, 1, 2 condition values but the others can be
// achieved by swapping operand order.
// arg0 ? arg1 : arg2 with conditions LT, GT, EQ
// arg0 ? arg2 : arg1 for conditions GE, LE, NE
// ISELB is used when a boolean result is needed, returning 0 or 1
p := s.Prog(ppc64.AISEL)
p.To.Type = obj.TYPE_REG
p.To.Reg = iselRegs[1]
iop := iselOps[v.Op]
ssaGenISEL(s, v, iop.cond, iselRegs[iop.valueIfCond], iselRegs[1-iop.valueIfCond])
case ssa.OpPPC64FLessEqual, // These include a second branch for EQ -- dealing with NaN prevents REL= to !REL conversion
ssa.OpPPC64FGreaterEqual:
p := s.Prog(ppc64.AMOVD)
p.To.Reg = v.Reg()
// For ISELB, boolean result 0 or 1. Use R0 for 0 operand to avoid load.
r := obj.Addr{Type: obj.TYPE_REG, Reg: ppc64.REG_R0}
if v.Op == ssa.OpPPC64ISEL {
r.Reg = v.Args[1].Reg()
}
// AuxInt values 4,5,6 implemented with reverse operand order from 0,1,2
if v.AuxInt > 3 {
p.Reg = r.Reg
p.SetFrom3(obj.Addr{Type: obj.TYPE_REG, Reg: v.Args[0].Reg()})
} else {
p.Reg = v.Args[0].Reg()
p.SetFrom3(r)
}
p.From.Type = obj.TYPE_CONST
p.From.Offset = 1
p.To.Type = obj.TYPE_REG
p.To.Reg = iselRegs[1]
iop := iselOps[v.Op]
ssaGenISEL(s, v, iop.cond, iselRegs[iop.valueIfCond], iselRegs[1-iop.valueIfCond])
ssaGenISEL(s, v, ppc64.C_COND_EQ, iselRegs[1], v.Reg())
p.From.Offset = v.AuxInt & 3
case ssa.OpPPC64LoweredZero:
......@@ -1265,6 +1221,11 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
gc.Warnl(v.Pos, "generated nil check")
}
// These should be resolved by rules and not make it here.
case ssa.OpPPC64Equal, ssa.OpPPC64NotEqual, ssa.OpPPC64LessThan, ssa.OpPPC64FLessThan,
ssa.OpPPC64LessEqual, ssa.OpPPC64GreaterThan, ssa.OpPPC64FGreaterThan, ssa.OpPPC64GreaterEqual,
ssa.OpPPC64FLessEqual, ssa.OpPPC64FGreaterEqual:
v.Fatalf("Pseudo-op should not make it to codegen: %s ###\n", v.LongString())
case ssa.OpPPC64InvertFlags:
v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
case ssa.OpPPC64FlagEQ, ssa.OpPPC64FlagLT, ssa.OpPPC64FlagGT:
......
......@@ -542,6 +542,9 @@
((EQ|NE|LT|LE|GT|GE) (CMPconst [0] z:(OR x y)) yes no) && z.Uses == 1 -> ((EQ|NE|LT|LE|GT|GE) (ORCC x y) yes no)
((EQ|NE|LT|LE|GT|GE) (CMPconst [0] z:(XOR x y)) yes no) && z.Uses == 1 -> ((EQ|NE|LT|LE|GT|GE) (XORCC x y) yes no)
(CondSelect x y bool) && flagArg(bool) != nil -> (ISEL [2] x y bool)
(CondSelect x y bool) && flagArg(bool) == nil -> (ISEL [2] x y (CMPWconst [0] bool))
// Lowering loads
(Load <t> ptr mem) && (is64BitInt(t) || isPtr(t)) -> (MOVDload ptr mem)
(Load <t> ptr mem) && is32BitInt(t) && isSigned(t) -> (MOVWload ptr mem)
......@@ -1019,6 +1022,59 @@
(CMPWU x (MOVDconst [c])) && isU16Bit(c) -> (CMPWUconst x [c])
(CMPWU (MOVDconst [c]) y) && isU16Bit(c) -> (InvertFlags (CMPWUconst y [c]))
// ISEL auxInt values 0=LT 1=GT 2=EQ arg2 ? arg0 : arg1
// ISEL auxInt values 4=GE 5=LE 6=NE arg2 ? arg1 : arg0
// ISELB special case where arg0, arg1 values are 0, 1
(Equal cmp) -> (ISELB [2] (MOVDconst [1]) cmp)
(NotEqual cmp) -> (ISELB [6] (MOVDconst [1]) cmp)
(LessThan cmp) -> (ISELB [0] (MOVDconst [1]) cmp)
(FLessThan cmp) -> (ISELB [0] (MOVDconst [1]) cmp)
(FLessEqual cmp) -> (ISEL [2] (MOVDconst [1]) (ISELB [0] (MOVDconst [1]) cmp) cmp)
(GreaterEqual cmp) -> (ISELB [4] (MOVDconst [1]) cmp)
(GreaterThan cmp) -> (ISELB [1] (MOVDconst [1]) cmp)
(FGreaterThan cmp) -> (ISELB [1] (MOVDconst [1]) cmp)
(FGreaterEqual cmp) -> (ISEL [2] (MOVDconst [1]) (ISELB [1] (MOVDconst [1]) cmp) cmp)
(LessEqual cmp) -> (ISELB [5] (MOVDconst [1]) cmp)
(ISELB [0] _ (FlagLT)) -> (MOVDconst [1])
(ISELB [0] _ (Flag(GT|EQ))) -> (MOVDconst [0])
(ISELB [1] _ (FlagGT)) -> (MOVDconst [1])
(ISELB [1] _ (Flag(LT|EQ))) -> (MOVDconst [0])
(ISELB [2] _ (FlagEQ)) -> (MOVDconst [1])
(ISELB [2] _ (Flag(LT|GT))) -> (MOVDconst [0])
(ISELB [4] _ (FlagLT)) -> (MOVDconst [0])
(ISELB [4] _ (Flag(GT|EQ))) -> (MOVDconst [1])
(ISELB [5] _ (FlagGT)) -> (MOVDconst [0])
(ISELB [5] _ (Flag(LT|EQ))) -> (MOVDconst [1])
(ISELB [6] _ (FlagEQ)) -> (MOVDconst [0])
(ISELB [6] _ (Flag(LT|GT))) -> (MOVDconst [1])
(ISEL [2] x _ (FlagEQ)) -> x
(ISEL [2] _ y (Flag(LT|GT))) -> y
(ISEL [6] _ y (FlagEQ)) -> y
(ISEL [6] x _ (Flag(LT|GT))) -> x
(ISEL [0] _ y (Flag(EQ|GT))) -> y
(ISEL [0] x _ (FlagLT)) -> x
(ISEL [5] _ x (Flag(EQ|LT))) -> x
(ISEL [5] y _ (FlagGT)) -> y
(ISEL [1] _ y (Flag(EQ|LT))) -> y
(ISEL [1] x _ (FlagGT)) -> x
(ISEL [4] x _ (Flag(EQ|GT))) -> x
(ISEL [4] _ y (FlagLT)) -> y
(ISELB [n] (MOVDconst [1]) (InvertFlags bool)) && n%4 == 0 -> (ISELB [n+1] (MOVDconst [1]) bool)
(ISELB [n] (MOVDconst [1]) (InvertFlags bool)) && n%4 == 1 -> (ISELB [n-1] (MOVDconst [1]) bool)
(ISELB [n] (MOVDconst [1]) (InvertFlags bool)) && n%4 == 2 -> (ISELB [n] (MOVDconst [1]) bool)
(ISEL [n] x y (InvertFlags bool)) && n%4 == 0 -> (ISEL [n+1] x y bool)
(ISEL [n] x y (InvertFlags bool)) && n%4 == 1 -> (ISEL [n-1] x y bool)
(ISEL [n] x y (InvertFlags bool)) && n%4 == 2 -> (ISEL [n] x y bool)
// A particular pattern seen in cgo code:
(AND (MOVDconst [c]) x:(MOVBZload _ _)) -> (ANDconst [c&0xFF] x)
(AND x:(MOVBZload _ _) (MOVDconst [c])) -> (ANDconst [c&0xFF] x)
......
......@@ -140,6 +140,8 @@ func init() {
gp1cr = regInfo{inputs: []regMask{gp | sp | sb}}
gp2cr = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}}
crgp = regInfo{inputs: nil, outputs: []regMask{gp}}
crgp11 = regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}
crgp21 = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp}}
gpload = regInfo{inputs: []regMask{gp | sp | sb}, outputs: []regMask{gp}}
gploadidx = regInfo{inputs: []regMask{gp | sp | sb, gp}, outputs: []regMask{gp}}
gpstore = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}}
......@@ -365,6 +367,12 @@ func init() {
{name: "CMPWconst", argLength: 1, reg: gp1cr, asm: "CMPW", aux: "Int32", typ: "Flags"},
{name: "CMPWUconst", argLength: 1, reg: gp1cr, asm: "CMPWU", aux: "Int32", typ: "Flags"},
// ISEL auxInt values 0=LT 1=GT 2=EQ arg2 ? arg0 : arg1
// ISEL auxInt values 4=GE 5=LE 6=NE arg2 ? arg1 : arg0
// ISELB special case where arg0, arg1 values are 0, 1 for boolean result
{name: "ISEL", argLength: 3, reg: crgp21, asm: "ISEL", aux: "Int32", typ: "Int32"}, // see above
{name: "ISELB", argLength: 2, reg: crgp11, asm: "ISEL", aux: "Int32", typ: "Int32"}, // see above
// pseudo-ops
{name: "Equal", argLength: 1, reg: crgp}, // bool, true flags encode x==y false otherwise.
{name: "NotEqual", argLength: 1, reg: crgp}, // bool, true flags encode x!=y false otherwise.
......
......@@ -1811,6 +1811,8 @@ const (
OpPPC64CMPUconst
OpPPC64CMPWconst
OpPPC64CMPWUconst
OpPPC64ISEL
OpPPC64ISELB
OpPPC64Equal
OpPPC64NotEqual
OpPPC64LessThan
......@@ -24212,6 +24214,35 @@ var opcodeTable = [...]opInfo{
},
},
},
{
name: "ISEL",
auxType: auxInt32,
argLen: 3,
asm: ppc64.AISEL,
reg: regInfo{
inputs: []inputInfo{
{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
{1, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
},
outputs: []outputInfo{
{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
},
},
},
{
name: "ISELB",
auxType: auxInt32,
argLen: 2,
asm: ppc64.AISEL,
reg: regInfo{
inputs: []inputInfo{
{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
},
outputs: []outputInfo{
{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
},
},
},
{
name: "Equal",
argLen: 1,
......
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