Commit 5d9c7820 authored by Michael Munday's avatar Michael Munday

cmd/compile: allow R11 to be allocated on s390x

R11 is only used as a temporary by a very small set of instructions
(DIV, MOD, MULH and extended MVC/XC instructions). By marking these
instructions as clobbering R11 we can allocate R11 in the general
case.

Change-Id: I0d4ffe80e57c164d42a5ea5ef6308756a5b0f742
Reviewed-on: https://go-review.googlesource.com/110255
Run-TryBot: Michael Munday <mike.munday@ibm.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent d29ec40e
...@@ -110,12 +110,13 @@ func init() { ...@@ -110,12 +110,13 @@ func init() {
// Common individual register masks // Common individual register masks
var ( var (
sp = buildReg("SP") sp = buildReg("SP")
sb = buildReg("SB") sb = buildReg("SB")
r0 = buildReg("R0") r0 = buildReg("R0")
tmp = buildReg("R11") // R11 is used as a temporary in a small number of instructions.
// R10 and R11 are reserved by the assembler. // R10 is reserved by the assembler.
gp = buildReg("R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14") gp = buildReg("R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14")
gpg = gp | buildReg("g") gpg = gp | buildReg("g")
gpsp = gp | sp gpsp = gp | sp
...@@ -135,11 +136,12 @@ func init() { ...@@ -135,11 +136,12 @@ func init() {
// Common regInfo // Common regInfo
var ( var (
gp01 = regInfo{inputs: []regMask{}, outputs: gponly} gp01 = regInfo{inputs: []regMask{}, outputs: gponly}
gp11 = regInfo{inputs: []regMask{gp}, outputs: gponly} gp11 = regInfo{inputs: []regMask{gp}, outputs: gponly}
gp11sp = regInfo{inputs: []regMask{gpsp}, outputs: gponly} gp11sp = regInfo{inputs: []regMask{gpsp}, outputs: gponly}
gp21 = regInfo{inputs: []regMask{gp, gp}, outputs: gponly} gp21 = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
gp21sp = regInfo{inputs: []regMask{gpsp, gp}, outputs: gponly} gp21sp = regInfo{inputs: []regMask{gpsp, gp}, outputs: gponly}
gp21tmp = regInfo{inputs: []regMask{gp &^ tmp, gp &^ tmp}, outputs: []regMask{gp &^ tmp}, clobbers: tmp}
// R0 evaluates to 0 when used as the number of bits to shift // R0 evaluates to 0 when used as the number of bits to shift
// so we need to exclude it from that operand. // so we need to exclude it from that operand.
...@@ -255,19 +257,19 @@ func init() { ...@@ -255,19 +257,19 @@ func init() {
{name: "MULLDload", argLength: 3, reg: gpopload, asm: "MULLD", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 * *arg1. arg2=mem {name: "MULLDload", argLength: 3, reg: gpopload, asm: "MULLD", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 * *arg1. arg2=mem
{name: "MULLWload", argLength: 3, reg: gpopload, asm: "MULLW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 * *arg1. arg2=mem {name: "MULLWload", argLength: 3, reg: gpopload, asm: "MULLW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 * *arg1. arg2=mem
{name: "MULHD", argLength: 2, reg: gp21, asm: "MULHD", typ: "Int64", commutative: true, resultInArg0: true, clobberFlags: true}, // (arg0 * arg1) >> width {name: "MULHD", argLength: 2, reg: gp21tmp, asm: "MULHD", typ: "Int64", commutative: true, resultInArg0: true, clobberFlags: true}, // (arg0 * arg1) >> width
{name: "MULHDU", argLength: 2, reg: gp21, asm: "MULHDU", typ: "Int64", commutative: true, resultInArg0: true, clobberFlags: true}, // (arg0 * arg1) >> width {name: "MULHDU", argLength: 2, reg: gp21tmp, asm: "MULHDU", typ: "Int64", commutative: true, resultInArg0: true, clobberFlags: true}, // (arg0 * arg1) >> width
{name: "DIVD", argLength: 2, reg: gp21, asm: "DIVD", resultInArg0: true, clobberFlags: true}, // arg0 / arg1 {name: "DIVD", argLength: 2, reg: gp21tmp, asm: "DIVD", resultInArg0: true, clobberFlags: true}, // arg0 / arg1
{name: "DIVW", argLength: 2, reg: gp21, asm: "DIVW", resultInArg0: true, clobberFlags: true}, // arg0 / arg1 {name: "DIVW", argLength: 2, reg: gp21tmp, asm: "DIVW", resultInArg0: true, clobberFlags: true}, // arg0 / arg1
{name: "DIVDU", argLength: 2, reg: gp21, asm: "DIVDU", resultInArg0: true, clobberFlags: true}, // arg0 / arg1 {name: "DIVDU", argLength: 2, reg: gp21tmp, asm: "DIVDU", resultInArg0: true, clobberFlags: true}, // arg0 / arg1
{name: "DIVWU", argLength: 2, reg: gp21, asm: "DIVWU", resultInArg0: true, clobberFlags: true}, // arg0 / arg1 {name: "DIVWU", argLength: 2, reg: gp21tmp, asm: "DIVWU", resultInArg0: true, clobberFlags: true}, // arg0 / arg1
{name: "MODD", argLength: 2, reg: gp21, asm: "MODD", resultInArg0: true, clobberFlags: true}, // arg0 % arg1 {name: "MODD", argLength: 2, reg: gp21tmp, asm: "MODD", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
{name: "MODW", argLength: 2, reg: gp21, asm: "MODW", resultInArg0: true, clobberFlags: true}, // arg0 % arg1 {name: "MODW", argLength: 2, reg: gp21tmp, asm: "MODW", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
{name: "MODDU", argLength: 2, reg: gp21, asm: "MODDU", resultInArg0: true, clobberFlags: true}, // arg0 % arg1 {name: "MODDU", argLength: 2, reg: gp21tmp, asm: "MODDU", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
{name: "MODWU", argLength: 2, reg: gp21, asm: "MODWU", resultInArg0: true, clobberFlags: true}, // arg0 % arg1 {name: "MODWU", argLength: 2, reg: gp21tmp, asm: "MODWU", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true, clobberFlags: true}, // arg0 & arg1 {name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true, clobberFlags: true}, // arg0 & arg1
{name: "ANDW", argLength: 2, reg: gp21, asm: "ANDW", commutative: true, clobberFlags: true}, // arg0 & arg1 {name: "ANDW", argLength: 2, reg: gp21, asm: "ANDW", commutative: true, clobberFlags: true}, // arg0 & arg1
......
This diff is collapsed.
...@@ -589,7 +589,7 @@ func (s *regAllocState) init(f *Func) { ...@@ -589,7 +589,7 @@ func (s *regAllocState) init(f *Func) {
// in the rewrite rules so we always have a free register // in the rewrite rules so we always have a free register
// available for global load/stores. See gen/386.rules (search for Flag_shared). // available for global load/stores. See gen/386.rules (search for Flag_shared).
case "s390x": case "s390x":
// nothing to do, R10 & R11 already reserved s.allocatable &^= 1 << 11 // R11
default: default:
s.f.fe.Fatalf(src.NoXPos, "arch %s not implemented", s.f.Config.arch) s.f.fe.Fatalf(src.NoXPos, "arch %s not implemented", s.f.Config.arch)
} }
......
...@@ -825,30 +825,29 @@ TEXT ·checkASM(SB),NOSPLIT,$0-1 ...@@ -825,30 +825,29 @@ TEXT ·checkASM(SB),NOSPLIT,$0-1
// gcWriteBarrier does NOT follow the Go ABI. It takes two arguments: // gcWriteBarrier does NOT follow the Go ABI. It takes two arguments:
// - R2 is the destination of the write // - R2 is the destination of the write
// - R3 is the value being written at R2. // - R3 is the value being written at R2.
// It clobbers R10 and R11 (the linker temp registers). // It clobbers R10 (the temp register).
// It does not clobber any other general-purpose registers, // It does not clobber any other general-purpose registers,
// but may clobber others (e.g., floating point registers). // but may clobber others (e.g., floating point registers).
TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$88 TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$104
// Save the registers clobbered by the fast path. // Save the registers clobbered by the fast path.
MOVD R1, 80(R15) MOVD R1, 96(R15)
MOVD R4, 88(R15) MOVD R4, 104(R15)
MOVD g_m(g), R1 MOVD g_m(g), R1
MOVD m_p(R1), R1 MOVD m_p(R1), R1
MOVD (p_wbBuf+wbBuf_next)(R1), R4
// Increment wbBuf.next position. // Increment wbBuf.next position.
ADD $16, R4 MOVD $16, R4
ADD (p_wbBuf+wbBuf_next)(R1), R4
MOVD R4, (p_wbBuf+wbBuf_next)(R1) MOVD R4, (p_wbBuf+wbBuf_next)(R1)
MOVD (p_wbBuf+wbBuf_end)(R1), R1 MOVD (p_wbBuf+wbBuf_end)(R1), R1
MOVD R1, R10 // R10 is linker temp register
// Record the write. // Record the write.
MOVD R3, -16(R4) // Record value MOVD R3, -16(R4) // Record value
MOVD (R2), R1 // TODO: This turns bad writes into bad reads. MOVD (R2), R10 // TODO: This turns bad writes into bad reads.
MOVD R1, -8(R4) // Record *slot MOVD R10, -8(R4) // Record *slot
// Is the buffer full? // Is the buffer full?
CMPBEQ R4, R10, flush CMPBEQ R4, R1, flush
ret: ret:
MOVD 80(R15), R1 MOVD 96(R15), R1
MOVD 88(R15), R4 MOVD 104(R15), R4
// Do the write. // Do the write.
MOVD R3, (R2) MOVD R3, (R2)
RET RET
...@@ -856,18 +855,11 @@ ret: ...@@ -856,18 +855,11 @@ ret:
flush: flush:
// Save all general purpose registers since these could be // Save all general purpose registers since these could be
// clobbered by wbBufFlush and were not saved by the caller. // clobbered by wbBufFlush and were not saved by the caller.
MOVD R2, 8(R15) // Also first argument to wbBufFlush STMG R2, R3, 8(R15) // set R2 and R3 as arguments for wbBufFlush
MOVD R3, 16(R15) // Also second argument to wbBufFlush
MOVD R0, 24(R15) MOVD R0, 24(R15)
// R1 already saved. // R1 already saved.
// R4 already saved. // R4 already saved.
MOVD R5, 32(R15) STMG R5, R12, 32(R15) // save R5 - R12
MOVD R6, 40(R15)
MOVD R7, 48(R15)
MOVD R8, 56(R15)
MOVD R9, 64(R15)
// R10 and R11 are linker temp registers.
MOVD R12, 72(R15)
// R13 is g. // R13 is g.
// R14 is LR. // R14 is LR.
// R15 is SP. // R15 is SP.
...@@ -875,13 +867,7 @@ flush: ...@@ -875,13 +867,7 @@ flush:
// This takes arguments R2 and R3. // This takes arguments R2 and R3.
CALL runtime·wbBufFlush(SB) CALL runtime·wbBufFlush(SB)
MOVD 8(R15), R2 LMG 8(R15), R2, R3 // restore R2 - R3
MOVD 16(R15), R3 MOVD 24(R15), R0 // restore R0
MOVD 24(R15), R0 LMG 32(R15), R5, R12 // restore R5 - R12
MOVD 32(R15), R5
MOVD 40(R15), R6
MOVD 48(R15), R7
MOVD 56(R15), R8
MOVD 64(R15), R9
MOVD 72(R15), R12
JMP ret JMP ret
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