1
0
mirror of https://github.com/golang/go synced 2024-10-01 16:28:33 -06:00

cmd/asm: add masked branch and conditional load instructions to s390x

The branch-relative-on-condition (BRC) instruction allows us to use
an immediate to specify under what conditions the branch is taken.
For example, `BRC $7, L1` is equivalent to `BNE L1`. It is sometimes
useful to specify branches in this way when either we don't have
an extended mnemonic for a particular mask value or we want to
generate the condition code mask programmatically.

The new load-on-condition (LOCR and LOCGR) and compare-and-branch
(CRJ, CGRJ, CLRJ, CLGRJ, CIJ, CGIJ, CLIJ and CLGIJ) instructions
provide the same flexibility for conditional loads and combined
compare and branch instructions.

Change-Id: Ic6f5d399b0157e278b39bd3645f4ee0f4df8e5fc
Reviewed-on: https://go-review.googlesource.com/c/go/+/196558
Run-TryBot: Michael Munday <mike.munday@ibm.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
This commit is contained in:
Michael Munday 2019-08-15 20:43:46 +01:00
parent eb96f8a574
commit 8c99e45ef9
7 changed files with 170 additions and 28 deletions

View File

@ -15,7 +15,8 @@ import (
func jumpS390x(word string) bool { func jumpS390x(word string) bool {
switch word { switch word {
case "BC", case "BRC",
"BC",
"BCL", "BCL",
"BEQ", "BEQ",
"BGE", "BGE",
@ -41,6 +42,14 @@ func jumpS390x(word string) bool {
"CMPUBLE", "CMPUBLE",
"CMPUBLT", "CMPUBLT",
"CMPUBNE", "CMPUBNE",
"CRJ",
"CGRJ",
"CLRJ",
"CLGRJ",
"CIJ",
"CGIJ",
"CLIJ",
"CLGIJ",
"CALL", "CALL",
"JMP": "JMP":
return true return true

View File

@ -450,8 +450,19 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) {
target = &a[2] target = &a[2]
break break
} }
p.errorf("wrong number of arguments to %s instruction", op)
fallthrough return
case 4:
if p.arch.Family == sys.S390X {
// 4-operand compare-and-branch.
prog.From = a[0]
prog.Reg = p.getRegister(prog, op, &a[1])
prog.SetFrom3(a[2])
target = &a[3]
break
}
p.errorf("wrong number of arguments to %s instruction", op)
return
default: default:
p.errorf("wrong number of arguments to %s instruction", op) p.errorf("wrong number of arguments to %s instruction", op)
return return

View File

@ -22,6 +22,9 @@ TEXT main·foo(SB),DUPOK|NOSPLIT,$16-0 // TEXT main.foo(SB), DUPOK|NOSPLIT, $16-
MOVDLT R8, R9 // b9e24098 MOVDLT R8, R9 // b9e24098
MOVDNE R10, R11 // b9e270ba MOVDNE R10, R11 // b9e270ba
LOCR $3, R2, R1 // b9f23012
LOCGR $7, R5, R6 // b9e27065
MOVD (R15), R1 // e310f0000004 MOVD (R15), R1 // e310f0000004
MOVW (R15), R2 // e320f0000014 MOVW (R15), R2 // e320f0000014
MOVH (R15), R3 // e330f0000015 MOVH (R15), R3 // e330f0000015
@ -253,6 +256,7 @@ TEXT main·foo(SB),DUPOK|NOSPLIT,$16-0 // TEXT main.foo(SB), DUPOK|NOSPLIT, $16-
IPM R3 // b2220030 IPM R3 // b2220030
IPM R12 // b22200c0 IPM R12 // b22200c0
BRC $7, 0(PC) // a7740000
BNE 0(PC) // a7740000 BNE 0(PC) // a7740000
BEQ 0(PC) // a7840000 BEQ 0(PC) // a7840000
BLT 0(PC) // a7440000 BLT 0(PC) // a7440000
@ -290,6 +294,16 @@ TEXT main·foo(SB),DUPOK|NOSPLIT,$16-0 // TEXT main.foo(SB), DUPOK|NOSPLIT, $16-
CMPUBGT R9, $256, 0(PC) // ec920000007d CMPUBGT R9, $256, 0(PC) // ec920000007d
CMPUBGE R2, $0, 0(PC) // ec2a0000007d CMPUBGE R2, $0, 0(PC) // ec2a0000007d
CRJ $15, R1, R2, 0(PC) // ec120000f076
CGRJ $12, R3, R4, 0(PC) // ec340000c064
CLRJ $3, R5, R6, 0(PC) // ec5600003077
CLGRJ $0, R7, R8, 0(PC) // ec7800000065
CIJ $4, R9, $127, 0(PC) // ec9400007f7e
CGIJ $8, R11, $-128, 0(PC) // ecb80000807c
CLIJ $1, R1, $255, 0(PC) // ec110000ff7f
CLGIJ $2, R3, $0, 0(PC) // ec320000007d
LGDR F1, R12 // b3cd00c1 LGDR F1, R12 // b3cd00c1
LDGR R2, F15 // b3c100f2 LDGR R2, F15 // b3c100f2

View File

@ -268,6 +268,8 @@ const (
AMOVDLE AMOVDLE
AMOVDLT AMOVDLT
AMOVDNE AMOVDNE
ALOCR
ALOCGR
// find leftmost one // find leftmost one
AFLOGR AFLOGR
@ -394,6 +396,7 @@ const (
// branch // branch
ABC ABC
ABCL ABCL
ABRC
ABEQ ABEQ
ABGE ABGE
ABGT ABGT
@ -407,6 +410,14 @@ const (
ASYSCALL ASYSCALL
// compare and branch // compare and branch
ACRJ
ACGRJ
ACLRJ
ACLGRJ
ACIJ
ACGIJ
ACLIJ
ACLGIJ
ACMPBEQ ACMPBEQ
ACMPBGE ACMPBGE
ACMPBGT ACMPBGT

View File

@ -45,6 +45,8 @@ var Anames = []string{
"MOVDLE", "MOVDLE",
"MOVDLT", "MOVDLT",
"MOVDNE", "MOVDNE",
"LOCR",
"LOCGR",
"FLOGR", "FLOGR",
"POPCNT", "POPCNT",
"AND", "AND",
@ -141,6 +143,7 @@ var Anames = []string{
"SYNC", "SYNC",
"BC", "BC",
"BCL", "BCL",
"BRC",
"BEQ", "BEQ",
"BGE", "BGE",
"BGT", "BGT",
@ -152,6 +155,14 @@ var Anames = []string{
"BVC", "BVC",
"BVS", "BVS",
"SYSCALL", "SYSCALL",
"CRJ",
"CGRJ",
"CLRJ",
"CLGRJ",
"CIJ",
"CGIJ",
"CLIJ",
"CLGIJ",
"CMPBEQ", "CMPBEQ",
"CMPBGE", "CMPBGE",
"CMPBGT", "CMPBGT",

View File

@ -236,21 +236,32 @@ var optab = []Optab{
// branch // branch
{i: 16, as: ABEQ, a6: C_SBRA}, {i: 16, as: ABEQ, a6: C_SBRA},
{i: 16, as: ABRC, a1: C_SCON, a6: C_SBRA},
{i: 11, as: ABR, a6: C_LBRA}, {i: 11, as: ABR, a6: C_LBRA},
{i: 16, as: ABC, a1: C_SCON, a2: C_REG, a6: C_LBRA}, {i: 16, as: ABC, a1: C_SCON, a2: C_REG, a6: C_LBRA},
{i: 18, as: ABR, a6: C_REG}, {i: 18, as: ABR, a6: C_REG},
{i: 18, as: ABR, a1: C_REG, a6: C_REG}, {i: 18, as: ABR, a1: C_REG, a6: C_REG},
{i: 15, as: ABR, a6: C_ZOREG}, {i: 15, as: ABR, a6: C_ZOREG},
{i: 15, as: ABC, a6: C_ZOREG}, {i: 15, as: ABC, a6: C_ZOREG},
// compare and branch
{i: 89, as: ACGRJ, a1: C_SCON, a2: C_REG, a3: C_REG, a6: C_SBRA},
{i: 89, as: ACMPBEQ, a1: C_REG, a2: C_REG, a6: C_SBRA}, {i: 89, as: ACMPBEQ, a1: C_REG, a2: C_REG, a6: C_SBRA},
{i: 89, as: ACLGRJ, a1: C_SCON, a2: C_REG, a3: C_REG, a6: C_SBRA},
{i: 89, as: ACMPUBEQ, a1: C_REG, a2: C_REG, a6: C_SBRA},
{i: 90, as: ACGIJ, a1: C_SCON, a2: C_REG, a3: C_ADDCON, a6: C_SBRA},
{i: 90, as: ACGIJ, a1: C_SCON, a2: C_REG, a3: C_SCON, a6: C_SBRA},
{i: 90, as: ACMPBEQ, a1: C_REG, a3: C_ADDCON, a6: C_SBRA}, {i: 90, as: ACMPBEQ, a1: C_REG, a3: C_ADDCON, a6: C_SBRA},
{i: 90, as: ACMPBEQ, a1: C_REG, a3: C_SCON, a6: C_SBRA}, {i: 90, as: ACMPBEQ, a1: C_REG, a3: C_SCON, a6: C_SBRA},
{i: 89, as: ACMPUBEQ, a1: C_REG, a2: C_REG, a6: C_SBRA}, {i: 90, as: ACLGIJ, a1: C_SCON, a2: C_REG, a3: C_ADDCON, a6: C_SBRA},
{i: 90, as: ACMPUBEQ, a1: C_REG, a3: C_ANDCON, a6: C_SBRA}, {i: 90, as: ACMPUBEQ, a1: C_REG, a3: C_ANDCON, a6: C_SBRA},
// move on condition // move on condition
{i: 17, as: AMOVDEQ, a1: C_REG, a6: C_REG}, {i: 17, as: AMOVDEQ, a1: C_REG, a6: C_REG},
// load on condition
{i: 25, as: ALOCGR, a1: C_SCON, a2: C_REG, a6: C_REG},
// find leftmost one // find leftmost one
{i: 8, as: AFLOGR, a1: C_REG, a6: C_REG}, {i: 8, as: AFLOGR, a1: C_REG, a6: C_REG},
@ -1022,12 +1033,22 @@ func buildop(ctxt *obj.Link) {
opset(ACMPUBLE, r) opset(ACMPUBLE, r)
opset(ACMPUBLT, r) opset(ACMPUBLT, r)
opset(ACMPUBNE, r) opset(ACMPUBNE, r)
case ACGRJ:
opset(ACRJ, r)
case ACLGRJ:
opset(ACLRJ, r)
case ACGIJ:
opset(ACIJ, r)
case ACLGIJ:
opset(ACLIJ, r)
case AMOVDEQ: case AMOVDEQ:
opset(AMOVDGE, r) opset(AMOVDGE, r)
opset(AMOVDGT, r) opset(AMOVDGT, r)
opset(AMOVDLE, r) opset(AMOVDLE, r)
opset(AMOVDLT, r) opset(AMOVDLT, r)
opset(AMOVDNE, r) opset(AMOVDNE, r)
case ALOCGR:
opset(ALOCR, r)
case ALTDBR: case ALTDBR:
opset(ALTEBR, r) opset(ALTEBR, r)
case ATCDB: case ATCDB:
@ -2620,6 +2641,10 @@ func (c *ctxtz) addcallreloc(sym *obj.LSym, add int64) *obj.Reloc {
func (c *ctxtz) branchMask(p *obj.Prog) uint32 { func (c *ctxtz) branchMask(p *obj.Prog) uint32 {
switch p.As { switch p.As {
case ABRC, ALOCR, ALOCGR,
ACRJ, ACGRJ, ACIJ, ACGIJ,
ACLRJ, ACLGRJ, ACLIJ, ACLGIJ:
return uint32(p.From.Offset)
case ABEQ, ACMPBEQ, ACMPUBEQ, AMOVDEQ: case ABEQ, ACMPBEQ, ACMPUBEQ, AMOVDEQ:
return 0x8 return 0x8
case ABGE, ACMPBGE, ACMPUBGE, AMOVDGE: case ABGE, ACMPBGE, ACMPUBGE, AMOVDGE:
@ -3207,6 +3232,17 @@ func (c *ctxtz) asmout(p *obj.Prog, asm *[]byte) {
zRIL(_a, op_XILF, uint32(p.To.Reg), uint32(v), asm) zRIL(_a, op_XILF, uint32(p.To.Reg), uint32(v), asm)
} }
case 25: // load on condition (register)
m3 := c.branchMask(p)
var opcode uint32
switch p.As {
case ALOCR:
opcode = op_LOCR
case ALOCGR:
opcode = op_LOCGR
}
zRRF(opcode, m3, 0, uint32(p.To.Reg), uint32(p.Reg), asm)
case 26: // MOVD $offset(base)(index), reg case 26: // MOVD $offset(base)(index), reg
v := c.regoff(&p.From) v := c.regoff(&p.From)
r := p.From.Reg r := p.From.Reg
@ -3788,21 +3824,44 @@ func (c *ctxtz) asmout(p *obj.Prog, asm *[]byte) {
if p.Pcond != nil { if p.Pcond != nil {
v = int32((p.Pcond.Pc - p.Pc) >> 1) v = int32((p.Pcond.Pc - p.Pc) >> 1)
} }
var opcode, opcode2 uint32
switch p.As { // Some instructions take a mask as the first argument.
case ACMPBEQ, ACMPBGE, ACMPBGT, ACMPBLE, ACMPBLT, ACMPBNE: r1, r2 := p.From.Reg, p.Reg
opcode = op_CGRJ if p.From.Type == obj.TYPE_CONST {
opcode2 = op_CGR r1, r2 = p.Reg, p.RestArgs[0].Reg
case ACMPUBEQ, ACMPUBGE, ACMPUBGT, ACMPUBLE, ACMPUBLT, ACMPUBNE:
opcode = op_CLGRJ
opcode2 = op_CLGR
} }
mask := c.branchMask(p) m3 := c.branchMask(p)
var opcode uint32
switch p.As {
case ACRJ:
// COMPARE AND BRANCH RELATIVE (32)
opcode = op_CRJ
case ACGRJ, ACMPBEQ, ACMPBGE, ACMPBGT, ACMPBLE, ACMPBLT, ACMPBNE:
// COMPARE AND BRANCH RELATIVE (64)
opcode = op_CGRJ
case ACLRJ:
// COMPARE LOGICAL AND BRANCH RELATIVE (32)
opcode = op_CLRJ
case ACLGRJ, ACMPUBEQ, ACMPUBGE, ACMPUBGT, ACMPUBLE, ACMPUBLT, ACMPUBNE:
// COMPARE LOGICAL AND BRANCH RELATIVE (64)
opcode = op_CLGRJ
}
if int32(int16(v)) != v { if int32(int16(v)) != v {
zRRE(opcode2, uint32(p.From.Reg), uint32(p.Reg), asm) // The branch is too far for one instruction so crack
zRIL(_c, op_BRCL, mask, uint32(v-sizeRRE/2), asm) // `CMPBEQ x, y, target` into:
//
// CMPBNE x, y, 2(PC)
// BR target
//
// Note that the instruction sequence MUST NOT clobber
// the condition code.
m3 ^= 0xe // invert 3-bit mask
zRIE(_b, opcode, uint32(r1), uint32(r2), uint32(sizeRIE+sizeRIL)/2, 0, 0, m3, 0, asm)
zRIL(_c, op_BRCL, 0xf, uint32(v-sizeRIE/2), asm)
} else { } else {
zRIE(_b, opcode, uint32(p.From.Reg), uint32(p.Reg), uint32(v), 0, 0, mask, 0, asm) zRIE(_b, opcode, uint32(r1), uint32(r2), uint32(v), 0, 0, m3, 0, asm)
} }
case 90: // compare and branch reg $constant case 90: // compare and branch reg $constant
@ -3810,21 +3869,39 @@ func (c *ctxtz) asmout(p *obj.Prog, asm *[]byte) {
if p.Pcond != nil { if p.Pcond != nil {
v = int32((p.Pcond.Pc - p.Pc) >> 1) v = int32((p.Pcond.Pc - p.Pc) >> 1)
} }
var opcode, opcode2 uint32
switch p.As { // Some instructions take a mask as the first argument.
case ACMPBEQ, ACMPBGE, ACMPBGT, ACMPBLE, ACMPBLT, ACMPBNE: r1, i2 := p.From.Reg, p.RestArgs[0].Offset
opcode = op_CGIJ if p.From.Type == obj.TYPE_CONST {
opcode2 = op_CGFI r1 = p.Reg
case ACMPUBEQ, ACMPUBGE, ACMPUBGT, ACMPUBLE, ACMPUBLT, ACMPUBNE: }
opcode = op_CLGIJ m3 := c.branchMask(p)
opcode2 = op_CLGFI
var opcode uint32
switch p.As {
case ACIJ:
opcode = op_CIJ
case ACGIJ, ACMPBEQ, ACMPBGE, ACMPBGT, ACMPBLE, ACMPBLT, ACMPBNE:
opcode = op_CGIJ
case ACLIJ:
opcode = op_CLIJ
case ACLGIJ, ACMPUBEQ, ACMPUBGE, ACMPUBGT, ACMPUBLE, ACMPUBLT, ACMPUBNE:
opcode = op_CLGIJ
} }
mask := c.branchMask(p)
if int32(int16(v)) != v { if int32(int16(v)) != v {
zRIL(_a, opcode2, uint32(p.From.Reg), uint32(c.regoff(p.GetFrom3())), asm) // The branch is too far for one instruction so crack
zRIL(_c, op_BRCL, mask, uint32(v-sizeRIL/2), asm) // `CMPBEQ x, $0, target` into:
//
// CMPBNE x, $0, 2(PC)
// BR target
//
// Note that the instruction sequence MUST NOT clobber
// the condition code.
m3 ^= 0xe // invert 3-bit mask
zRIE(_c, opcode, uint32(r1), m3, uint32(sizeRIE+sizeRIL)/2, 0, 0, 0, uint32(i2), asm)
zRIL(_c, op_BRCL, 0xf, uint32(v-sizeRIE/2), asm)
} else { } else {
zRIE(_c, opcode, uint32(p.From.Reg), mask, uint32(v), 0, 0, 0, uint32(c.regoff(p.GetFrom3())), asm) zRIE(_c, opcode, uint32(r1), m3, uint32(v), 0, 0, 0, uint32(i2), asm)
} }
case 91: // test under mask (immediate) case 91: // test under mask (immediate)

View File

@ -249,6 +249,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
fallthrough fallthrough
case ABC, case ABC,
ABRC,
ABEQ, ABEQ,
ABGE, ABGE,
ABGT, ABGT,
@ -260,6 +261,14 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
ABR, ABR,
ABVC, ABVC,
ABVS, ABVS,
ACRJ,
ACGRJ,
ACLRJ,
ACLGRJ,
ACIJ,
ACGIJ,
ACLIJ,
ACLGIJ,
ACMPBEQ, ACMPBEQ,
ACMPBGE, ACMPBGE,
ACMPBGT, ACMPBGT,