cmd/compile: introduce CCMP generation

Introduce new aux type "ARM64ConditionalParams", which contains condition code, NZCV flags and constant with indicator of using it for CCMP instructions

Updates #71268

Change-Id: I322a6cb7077c9a2c4415893c5eb7ff7692d5a2de
Reviewed-on: https://go-review.googlesource.com/c/go/+/698037
Reviewed-by: Mark Freeman <markfreeman@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Keith Randall <khr@golang.org>
This commit is contained in:
Ch1n-ch1nless 2025-08-21 17:41:13 +03:00 committed by Gopher Robot
parent 3b3b16957c
commit 10ac80de77
8 changed files with 235 additions and 7 deletions

View file

@ -1050,6 +1050,27 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.From.Offset = int64(condCode) p.From.Offset = int64(condCode)
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg() p.To.Reg = v.Reg()
case ssa.OpARM64CCMP,
ssa.OpARM64CCMN,
ssa.OpARM64CCMPconst,
ssa.OpARM64CCMNconst,
ssa.OpARM64CCMPW,
ssa.OpARM64CCMNW,
ssa.OpARM64CCMPWconst,
ssa.OpARM64CCMNWconst:
p := s.Prog(v.Op.Asm())
p.Reg = v.Args[0].Reg()
params := v.AuxArm64ConditionalParams()
p.From.Type = obj.TYPE_SPECIAL // assembler encodes conditional bits in Offset
p.From.Offset = int64(condBits[params.Cond()])
constValue, ok := params.ConstValue()
if ok {
p.AddRestSourceConst(constValue)
} else {
p.AddRestSourceReg(v.Args[1].Reg())
}
p.To.Type = obj.TYPE_CONST
p.To.Offset = params.Nzcv()
case ssa.OpARM64LoweredZero: case ssa.OpARM64LoweredZero:
ptrReg := v.Args[0].Reg() ptrReg := v.Args[0].Reg()
n := v.AuxInt n := v.AuxInt

View file

@ -156,12 +156,14 @@ func init() {
gp11 = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}} gp11 = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
gp11sp = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}} gp11sp = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
gp1flags = regInfo{inputs: []regMask{gpg}} gp1flags = regInfo{inputs: []regMask{gpg}}
gp1flagsflags = regInfo{inputs: []regMask{gpg}}
gp1flags1 = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}} gp1flags1 = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
gp11flags = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp, 0}} gp11flags = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp, 0}}
gp21 = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}} gp21 = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}}
gp21nog = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp}} gp21nog = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp}}
gp21flags = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp, 0}} gp21flags = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp, 0}}
gp2flags = regInfo{inputs: []regMask{gpg, gpg}} gp2flags = regInfo{inputs: []regMask{gpg, gpg}}
gp2flagsflags = regInfo{inputs: []regMask{gpg, gpg}}
gp2flags1 = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp}} gp2flags1 = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp}}
gp2flags1flags = regInfo{inputs: []regMask{gp, gp, 0}, outputs: []regMask{gp, 0}} gp2flags1flags = regInfo{inputs: []regMask{gp, gp, 0}, outputs: []regMask{gp, 0}}
gp2load = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}} gp2load = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
@ -508,6 +510,22 @@ func init() {
{name: "CSNEG", argLength: 3, reg: gp2flags1, asm: "CSNEG", aux: "CCop"}, // auxint(flags) ? arg0 : -arg1 {name: "CSNEG", argLength: 3, reg: gp2flags1, asm: "CSNEG", aux: "CCop"}, // auxint(flags) ? arg0 : -arg1
{name: "CSETM", argLength: 1, reg: readflags, asm: "CSETM", aux: "CCop"}, // auxint(flags) ? -1 : 0 {name: "CSETM", argLength: 1, reg: readflags, asm: "CSETM", aux: "CCop"}, // auxint(flags) ? -1 : 0
// conditional comparison instructions; auxint is
// combination of Cond, Nzcv and optional ConstValue
// Behavior:
// If the condition 'Cond' evaluates to true against current flags,
// flags are set to the result of the comparison operation.
// Otherwise, flags are set to the fallback value 'Nzcv'.
{name: "CCMP", argLength: 3, reg: gp2flagsflags, asm: "CCMP", aux: "ARM64ConditionalParams", typ: "Flag"}, // If Cond then flags = CMP arg0 arg1 else flags = Nzcv
{name: "CCMN", argLength: 3, reg: gp2flagsflags, asm: "CCMN", aux: "ARM64ConditionalParams", typ: "Flag"}, // If Cond then flags = CMN arg0 arg1 else flags = Nzcv
{name: "CCMPconst", argLength: 2, reg: gp1flagsflags, asm: "CCMP", aux: "ARM64ConditionalParams", typ: "Flag"}, // If Cond then flags = CMPconst [ConstValue] arg0 else flags = Nzcv
{name: "CCMNconst", argLength: 2, reg: gp1flagsflags, asm: "CCMN", aux: "ARM64ConditionalParams", typ: "Flag"}, // If Cond then flags = CMNconst [ConstValue] arg0 else flags = Nzcv
{name: "CCMPW", argLength: 3, reg: gp2flagsflags, asm: "CCMPW", aux: "ARM64ConditionalParams", typ: "Flag"}, // If Cond then flags = CMPW arg0 arg1 else flags = Nzcv
{name: "CCMNW", argLength: 3, reg: gp2flagsflags, asm: "CCMNW", aux: "ARM64ConditionalParams", typ: "Flag"}, // If Cond then flags = CMNW arg0 arg1 else flags = Nzcv
{name: "CCMPWconst", argLength: 2, reg: gp1flagsflags, asm: "CCMPW", aux: "ARM64ConditionalParams", typ: "Flag"}, // If Cond then flags = CCMPWconst [ConstValue] arg0 else flags = Nzcv
{name: "CCMNWconst", argLength: 2, reg: gp1flagsflags, asm: "CCMNW", aux: "ARM64ConditionalParams", typ: "Flag"}, // If Cond then flags = CCMNWconst [ConstValue] arg0 else flags = Nzcv
// function calls // function calls
{name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem {name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem
{name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem {name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem

View file

@ -1439,7 +1439,7 @@ func opHasAuxInt(op opData) bool {
switch op.aux { switch op.aux {
case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "UInt8", "Float32", "Float64", case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "UInt8", "Float32", "Float64",
"SymOff", "CallOff", "SymValAndOff", "TypSize", "ARM64BitField", "FlagConstant", "CCop", "SymOff", "CallOff", "SymValAndOff", "TypSize", "ARM64BitField", "FlagConstant", "CCop",
"PanicBoundsC", "PanicBoundsCC": "PanicBoundsC", "PanicBoundsCC", "ARM64ConditionalParams":
return true return true
} }
return false return false
@ -1847,6 +1847,8 @@ func (op opData) auxIntType() string {
return "flagConstant" return "flagConstant"
case "ARM64BitField": case "ARM64BitField":
return "arm64BitField" return "arm64BitField"
case "ARM64ConditionalParams":
return "arm64ConditionalParams"
case "PanicBoundsC", "PanicBoundsCC": case "PanicBoundsC", "PanicBoundsCC":
return "int64" return "int64"
default: default:

View file

@ -145,7 +145,7 @@ func checkFunc(f *Func) {
f.Fatalf("bad int32 AuxInt value for %v", v) f.Fatalf("bad int32 AuxInt value for %v", v)
} }
canHaveAuxInt = true canHaveAuxInt = true
case auxInt64, auxARM64BitField: case auxInt64, auxARM64BitField, auxARM64ConditionalParams:
canHaveAuxInt = true canHaveAuxInt = true
case auxInt128: case auxInt128:
// AuxInt must be zero, so leave canHaveAuxInt set to false. // AuxInt must be zero, so leave canHaveAuxInt set to false.

View file

@ -375,11 +375,12 @@ const (
auxPanicBoundsCC // two constants for a bounds failure auxPanicBoundsCC // two constants for a bounds failure
// architecture specific aux types // architecture specific aux types
auxARM64BitField // aux is an arm64 bitfield lsb and width packed into auxInt auxARM64BitField // aux is an arm64 bitfield lsb and width packed into auxInt
auxS390XRotateParams // aux is a s390x rotate parameters object encoding start bit, end bit and rotate amount auxARM64ConditionalParams // aux is a structure, which contains condition, NZCV flags and constant with indicator of using it
auxS390XCCMask // aux is a s390x 4-bit condition code mask auxS390XRotateParams // aux is a s390x rotate parameters object encoding start bit, end bit and rotate amount
auxS390XCCMaskInt8 // aux is a s390x 4-bit condition code mask, auxInt is an int8 immediate auxS390XCCMask // aux is a s390x 4-bit condition code mask
auxS390XCCMaskUint8 // aux is a s390x 4-bit condition code mask, auxInt is a uint8 immediate auxS390XCCMaskInt8 // aux is a s390x 4-bit condition code mask, auxInt is an int8 immediate
auxS390XCCMaskUint8 // aux is a s390x 4-bit condition code mask, auxInt is a uint8 immediate
) )
// A SymEffect describes the effect that an SSA Value has on the variable // A SymEffect describes the effect that an SSA Value has on the variable
@ -534,3 +535,11 @@ func (b BoundsKind) Code() (rtabi.BoundsErrorCode, bool) {
// width+lsb<64 for 64-bit variant, width+lsb<32 for 32-bit variant. // width+lsb<64 for 64-bit variant, width+lsb<32 for 32-bit variant.
// the meaning of width and lsb are instruction-dependent. // the meaning of width and lsb are instruction-dependent.
type arm64BitField int16 type arm64BitField int16
// arm64ConditionalParams is the GO type of ARM64ConditionalParams auxInt.
type arm64ConditionalParams struct {
cond Op // Condition code to evaluate
nzcv uint8 // Fallback NZCV flags value when condition is false
constValue uint8 // Immediate value for constant comparisons
ind bool // Constant comparison indicator
}

View file

@ -1693,6 +1693,14 @@ const (
OpARM64CSINV OpARM64CSINV
OpARM64CSNEG OpARM64CSNEG
OpARM64CSETM OpARM64CSETM
OpARM64CCMP
OpARM64CCMN
OpARM64CCMPconst
OpARM64CCMNconst
OpARM64CCMPW
OpARM64CCMNW
OpARM64CCMPWconst
OpARM64CCMNWconst
OpARM64CALLstatic OpARM64CALLstatic
OpARM64CALLtail OpARM64CALLtail
OpARM64CALLclosure OpARM64CALLclosure
@ -22829,6 +22837,98 @@ var opcodeTable = [...]opInfo{
}, },
}, },
}, },
{
name: "CCMP",
auxType: auxARM64ConditionalParams,
argLen: 3,
asm: arm64.ACCMP,
reg: regInfo{
inputs: []inputInfo{
{0, 402653183}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
{1, 402653183}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
},
},
},
{
name: "CCMN",
auxType: auxARM64ConditionalParams,
argLen: 3,
asm: arm64.ACCMN,
reg: regInfo{
inputs: []inputInfo{
{0, 402653183}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
{1, 402653183}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
},
},
},
{
name: "CCMPconst",
auxType: auxARM64ConditionalParams,
argLen: 2,
asm: arm64.ACCMP,
reg: regInfo{
inputs: []inputInfo{
{0, 402653183}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
},
},
},
{
name: "CCMNconst",
auxType: auxARM64ConditionalParams,
argLen: 2,
asm: arm64.ACCMN,
reg: regInfo{
inputs: []inputInfo{
{0, 402653183}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
},
},
},
{
name: "CCMPW",
auxType: auxARM64ConditionalParams,
argLen: 3,
asm: arm64.ACCMPW,
reg: regInfo{
inputs: []inputInfo{
{0, 402653183}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
{1, 402653183}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
},
},
},
{
name: "CCMNW",
auxType: auxARM64ConditionalParams,
argLen: 3,
asm: arm64.ACCMNW,
reg: regInfo{
inputs: []inputInfo{
{0, 402653183}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
{1, 402653183}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
},
},
},
{
name: "CCMPWconst",
auxType: auxARM64ConditionalParams,
argLen: 2,
asm: arm64.ACCMPW,
reg: regInfo{
inputs: []inputInfo{
{0, 402653183}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
},
},
},
{
name: "CCMNWconst",
auxType: auxARM64ConditionalParams,
argLen: 2,
asm: arm64.ACCMNW,
reg: regInfo{
inputs: []inputInfo{
{0, 402653183}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
},
},
},
{ {
name: "CALLstatic", name: "CALLstatic",
auxType: auxCallOff, auxType: auxCallOff,

View file

@ -669,6 +669,17 @@ func auxIntToValAndOff(i int64) ValAndOff {
func auxIntToArm64BitField(i int64) arm64BitField { func auxIntToArm64BitField(i int64) arm64BitField {
return arm64BitField(i) return arm64BitField(i)
} }
func auxIntToArm64ConditionalParams(i int64) arm64ConditionalParams {
var params arm64ConditionalParams
params.cond = Op(i & 0xffff)
i >>= 16
params.nzcv = uint8(i & 0x0f)
i >>= 4
params.constValue = uint8(i & 0x1f)
i >>= 5
params.ind = i == 1
return params
}
func auxIntToFlagConstant(x int64) flagConstant { func auxIntToFlagConstant(x int64) flagConstant {
return flagConstant(x) return flagConstant(x)
} }
@ -710,6 +721,20 @@ func valAndOffToAuxInt(v ValAndOff) int64 {
func arm64BitFieldToAuxInt(v arm64BitField) int64 { func arm64BitFieldToAuxInt(v arm64BitField) int64 {
return int64(v) return int64(v)
} }
func arm64ConditionalParamsToAuxInt(v arm64ConditionalParams) int64 {
if v.cond&^0xffff != 0 {
panic("condition value exceeds 16 bits")
}
var i int64
if v.ind {
i = 1 << 25
}
i |= int64(v.constValue) << 20
i |= int64(v.nzcv) << 16
i |= int64(v.cond)
return i
}
func flagConstantToAuxInt(x flagConstant) int64 { func flagConstantToAuxInt(x flagConstant) int64 {
return int64(x) return int64(x)
} }
@ -1899,6 +1924,43 @@ func arm64BFWidth(mask, rshift int64) int64 {
return nto(shiftedMask) return nto(shiftedMask)
} }
// encodes condition code and NZCV flags into auxint.
func arm64ConditionalParamsAuxInt(cond Op, nzcv uint8) arm64ConditionalParams {
if cond < OpARM64Equal || cond > OpARM64GreaterEqualU {
panic("Wrong conditional operation")
}
if nzcv&0x0f != nzcv {
panic("Wrong value of NZCV flag")
}
return arm64ConditionalParams{cond, nzcv, 0, false}
}
// encodes condition code, NZCV flags and constant value into auxint.
func arm64ConditionalParamsAuxIntWithValue(cond Op, nzcv uint8, value uint8) arm64ConditionalParams {
if value&0x1f != value {
panic("Wrong value of constant")
}
params := arm64ConditionalParamsAuxInt(cond, nzcv)
params.constValue = value
params.ind = true
return params
}
// extracts condition code from auxint.
func (condParams arm64ConditionalParams) Cond() Op {
return condParams.cond
}
// extracts NZCV flags from auxint.
func (condParams arm64ConditionalParams) Nzcv() int64 {
return int64(condParams.nzcv)
}
// extracts constant value from auxint if present.
func (condParams arm64ConditionalParams) ConstValue() (int64, bool) {
return int64(condParams.constValue), condParams.ind
}
// registerizable reports whether t is a primitive type that fits in // registerizable reports whether t is a primitive type that fits in
// a register. It assumes float64 values will always fit into registers // a register. It assumes float64 values will always fit into registers
// even if that isn't strictly true. // even if that isn't strictly true.

View file

@ -144,6 +144,13 @@ func (v *Value) AuxArm64BitField() arm64BitField {
return arm64BitField(v.AuxInt) return arm64BitField(v.AuxInt)
} }
func (v *Value) AuxArm64ConditionalParams() arm64ConditionalParams {
if opcodeTable[v.Op].auxType != auxARM64ConditionalParams {
v.Fatalf("op %s doesn't have a ARM64ConditionalParams aux field", v.Op)
}
return auxIntToArm64ConditionalParams(v.AuxInt)
}
// long form print. v# = opcode <type> [aux] args [: reg] (names) // long form print. v# = opcode <type> [aux] args [: reg] (names)
func (v *Value) LongString() string { func (v *Value) LongString() string {
if v == nil { if v == nil {
@ -203,6 +210,15 @@ func (v *Value) auxString() string {
lsb := v.AuxArm64BitField().lsb() lsb := v.AuxArm64BitField().lsb()
width := v.AuxArm64BitField().width() width := v.AuxArm64BitField().width()
return fmt.Sprintf(" [lsb=%d,width=%d]", lsb, width) return fmt.Sprintf(" [lsb=%d,width=%d]", lsb, width)
case auxARM64ConditionalParams:
params := v.AuxArm64ConditionalParams()
cond := params.Cond()
nzcv := params.Nzcv()
imm, ok := params.ConstValue()
if ok {
return fmt.Sprintf(" [cond=%s,nzcv=%d,imm=%d]", cond, nzcv, imm)
}
return fmt.Sprintf(" [cond=%s,nzcv=%d]", cond, nzcv)
case auxFloat32, auxFloat64: case auxFloat32, auxFloat64:
return fmt.Sprintf(" [%g]", v.AuxFloat()) return fmt.Sprintf(" [%g]", v.AuxFloat())
case auxString: case auxString: