mirror of
https://github.com/golang/go
synced 2024-11-11 20:01:37 -07:00
cmd/compile: add SSA backend for s390x and enable by default
The new SSA backend modifies the ABI slightly: R0 is now a usable general purpose register. Fixes #16677. Change-Id: I367435ce921e0c7e79e021c80cf8ef5d1d1466cf Reviewed-on: https://go-review.googlesource.com/28978 Run-TryBot: Michael Munday <munday@ca.ibm.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
parent
b7e53038b8
commit
6ec993adc3
@ -40,7 +40,7 @@ func shouldssa(fn *Node) bool {
|
||||
if os.Getenv("SSATEST") == "" {
|
||||
return false
|
||||
}
|
||||
case "amd64", "amd64p32", "arm", "386", "arm64", "ppc64le", "mips64", "mips64le":
|
||||
case "amd64", "amd64p32", "arm", "386", "arm64", "ppc64le", "mips64", "mips64le", "s390x":
|
||||
// Generally available.
|
||||
}
|
||||
if !ssaEnabled {
|
||||
|
@ -58,6 +58,11 @@ func Main() {
|
||||
gc.Thearch.Doregbits = doregbits
|
||||
gc.Thearch.Regnames = regnames
|
||||
|
||||
gc.Thearch.SSARegToReg = ssaRegToReg
|
||||
gc.Thearch.SSAMarkMoves = ssaMarkMoves
|
||||
gc.Thearch.SSAGenValue = ssaGenValue
|
||||
gc.Thearch.SSAGenBlock = ssaGenBlock
|
||||
|
||||
gc.Main()
|
||||
gc.Exit(0)
|
||||
}
|
||||
|
@ -233,8 +233,8 @@ func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) {
|
||||
// Handle divide-by-zero panic.
|
||||
p1 := gins(optoas(gc.OCMP, t), &tr, nil)
|
||||
|
||||
p1.To.Type = obj.TYPE_REG
|
||||
p1.To.Reg = s390x.REGZERO
|
||||
p1.To.Type = obj.TYPE_CONST
|
||||
p1.To.Offset = 0
|
||||
p1 = gc.Gbranch(optoas(gc.ONE, t), nil, +1)
|
||||
if panicdiv == nil {
|
||||
panicdiv = gc.Sysfunc("panicdivide")
|
||||
@ -561,8 +561,8 @@ func expandchecks(firstp *obj.Prog) {
|
||||
// crash by write to memory address 0.
|
||||
p1.As = s390x.AMOVD
|
||||
|
||||
p1.From.Type = obj.TYPE_REG
|
||||
p1.From.Reg = s390x.REGZERO
|
||||
p1.From.Type = obj.TYPE_CONST
|
||||
p1.From.Offset = 0
|
||||
p1.To.Type = obj.TYPE_MEM
|
||||
p1.To.Reg = s390x.REGZERO
|
||||
p1.To.Offset = 0
|
||||
|
@ -36,23 +36,38 @@ var progtable = [s390x.ALAST & obj.AMask]obj.ProgInfo{
|
||||
// Integer
|
||||
s390x.AADD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.ASUB & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.ASUBE & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.AADDW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.ASUBW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.ANEG & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.ANEGW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.AAND & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.AOR & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.AXOR & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.AMULLD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.AMULLW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.AMULHD & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.AMULHDU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.AMULHD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.AMULHDU & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.ADIVD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.ADIVDU & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.ADIVW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.ADIVWU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.ASLD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.ASLW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.ASRD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.ASRW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.ASRAD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.ASRAW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.ARLL & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.ARLLG & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.ACMP & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightRead},
|
||||
s390x.ACMPU & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightRead},
|
||||
s390x.ACMPW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightRead},
|
||||
s390x.ACMPWU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightRead},
|
||||
s390x.AMODD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.AMODDU & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.AMODW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
s390x.AMODWU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
|
||||
// Floating point.
|
||||
s390x.AFADD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite},
|
||||
@ -68,6 +83,8 @@ var progtable = [s390x.ALAST & obj.AMask]obj.ProgInfo{
|
||||
s390x.ALEDBR & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv},
|
||||
s390x.ALDEBR & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv},
|
||||
s390x.AFSQRT & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite},
|
||||
s390x.AFNEG & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite},
|
||||
s390x.AFNEGS & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite},
|
||||
|
||||
// Conversions
|
||||
s390x.ACEFBRA & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv},
|
||||
@ -88,15 +105,24 @@ var progtable = [s390x.ALAST & obj.AMask]obj.ProgInfo{
|
||||
s390x.ACLGDBR & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv},
|
||||
|
||||
// Moves
|
||||
s390x.AMOVB & obj.AMask: {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
|
||||
s390x.AMOVBZ & obj.AMask: {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
|
||||
s390x.AMOVH & obj.AMask: {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
|
||||
s390x.AMOVHZ & obj.AMask: {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
|
||||
s390x.AMOVW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
|
||||
s390x.AMOVWZ & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
|
||||
s390x.AMOVD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
|
||||
s390x.AFMOVS & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
|
||||
s390x.AFMOVD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move},
|
||||
s390x.AMOVB & obj.AMask: {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
|
||||
s390x.AMOVBZ & obj.AMask: {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
|
||||
s390x.AMOVH & obj.AMask: {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
|
||||
s390x.AMOVHZ & obj.AMask: {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
|
||||
s390x.AMOVW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
|
||||
s390x.AMOVWZ & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
|
||||
s390x.AMOVD & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
|
||||
s390x.AMOVHBR & obj.AMask: {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
|
||||
s390x.AMOVWBR & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
|
||||
s390x.AMOVDBR & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
|
||||
s390x.AFMOVS & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
|
||||
s390x.AFMOVD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move},
|
||||
s390x.AMOVDEQ & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
|
||||
s390x.AMOVDGE & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
|
||||
s390x.AMOVDGT & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
|
||||
s390x.AMOVDLE & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
|
||||
s390x.AMOVDLT & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
|
||||
s390x.AMOVDNE & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
|
||||
|
||||
// Storage operations
|
||||
s390x.AMVC & obj.AMask: {Flags: gc.LeftRead | gc.LeftAddr | gc.RightWrite | gc.RightAddr},
|
||||
@ -114,6 +140,8 @@ var progtable = [s390x.ALAST & obj.AMask]obj.ProgInfo{
|
||||
s390x.ABLT & obj.AMask: {Flags: gc.Cjmp},
|
||||
s390x.ABGT & obj.AMask: {Flags: gc.Cjmp},
|
||||
s390x.ABLE & obj.AMask: {Flags: gc.Cjmp},
|
||||
s390x.ABLEU & obj.AMask: {Flags: gc.Cjmp},
|
||||
s390x.ABLTU & obj.AMask: {Flags: gc.Cjmp},
|
||||
s390x.ACMPBEQ & obj.AMask: {Flags: gc.Cjmp},
|
||||
s390x.ACMPBNE & obj.AMask: {Flags: gc.Cjmp},
|
||||
s390x.ACMPBGE & obj.AMask: {Flags: gc.Cjmp},
|
||||
|
885
src/cmd/compile/internal/s390x/ssa.go
Normal file
885
src/cmd/compile/internal/s390x/ssa.go
Normal file
@ -0,0 +1,885 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package s390x
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"cmd/compile/internal/gc"
|
||||
"cmd/compile/internal/ssa"
|
||||
"cmd/internal/obj"
|
||||
"cmd/internal/obj/s390x"
|
||||
)
|
||||
|
||||
// Smallest possible faulting page at address zero.
|
||||
const minZeroPage = 4096
|
||||
|
||||
// ssaRegToReg maps ssa register numbers to obj register numbers.
|
||||
var ssaRegToReg = []int16{
|
||||
s390x.REG_R0,
|
||||
s390x.REG_R1,
|
||||
s390x.REG_R2,
|
||||
s390x.REG_R3,
|
||||
s390x.REG_R4,
|
||||
s390x.REG_R5,
|
||||
s390x.REG_R6,
|
||||
s390x.REG_R7,
|
||||
s390x.REG_R8,
|
||||
s390x.REG_R9,
|
||||
s390x.REG_R10,
|
||||
s390x.REG_R11,
|
||||
s390x.REG_R12,
|
||||
s390x.REG_R13,
|
||||
s390x.REG_R14,
|
||||
s390x.REG_R15,
|
||||
s390x.REG_F0,
|
||||
s390x.REG_F1,
|
||||
s390x.REG_F2,
|
||||
s390x.REG_F3,
|
||||
s390x.REG_F4,
|
||||
s390x.REG_F5,
|
||||
s390x.REG_F6,
|
||||
s390x.REG_F7,
|
||||
s390x.REG_F8,
|
||||
s390x.REG_F9,
|
||||
s390x.REG_F10,
|
||||
s390x.REG_F11,
|
||||
s390x.REG_F12,
|
||||
s390x.REG_F13,
|
||||
s390x.REG_F14,
|
||||
s390x.REG_F15,
|
||||
0, // SB isn't a real register. We fill an Addr.Reg field with 0 in this case.
|
||||
}
|
||||
|
||||
// markMoves marks any MOVXconst ops that need to avoid clobbering flags.
|
||||
func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {
|
||||
flive := b.FlagsLiveAtEnd
|
||||
if b.Control != nil && b.Control.Type.IsFlags() {
|
||||
flive = true
|
||||
}
|
||||
for i := len(b.Values) - 1; i >= 0; i-- {
|
||||
v := b.Values[i]
|
||||
if flive && v.Op == ssa.OpS390XMOVDconst {
|
||||
// The "mark" is any non-nil Aux value.
|
||||
v.Aux = v
|
||||
}
|
||||
if v.Type.IsFlags() {
|
||||
flive = false
|
||||
}
|
||||
for _, a := range v.Args {
|
||||
if a.Type.IsFlags() {
|
||||
flive = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loadByType returns the load instruction of the given type.
|
||||
func loadByType(t ssa.Type) obj.As {
|
||||
if t.IsFloat() {
|
||||
switch t.Size() {
|
||||
case 4:
|
||||
return s390x.AFMOVS
|
||||
case 8:
|
||||
return s390x.AFMOVD
|
||||
}
|
||||
} else {
|
||||
switch t.Size() {
|
||||
case 1:
|
||||
if t.IsSigned() {
|
||||
return s390x.AMOVB
|
||||
} else {
|
||||
return s390x.AMOVBZ
|
||||
}
|
||||
case 2:
|
||||
if t.IsSigned() {
|
||||
return s390x.AMOVH
|
||||
} else {
|
||||
return s390x.AMOVHZ
|
||||
}
|
||||
case 4:
|
||||
if t.IsSigned() {
|
||||
return s390x.AMOVW
|
||||
} else {
|
||||
return s390x.AMOVWZ
|
||||
}
|
||||
case 8:
|
||||
return s390x.AMOVD
|
||||
}
|
||||
}
|
||||
panic("bad load type")
|
||||
}
|
||||
|
||||
// storeByType returns the store instruction of the given type.
|
||||
func storeByType(t ssa.Type) obj.As {
|
||||
width := t.Size()
|
||||
if t.IsFloat() {
|
||||
switch width {
|
||||
case 4:
|
||||
return s390x.AFMOVS
|
||||
case 8:
|
||||
return s390x.AFMOVD
|
||||
}
|
||||
} else {
|
||||
switch width {
|
||||
case 1:
|
||||
return s390x.AMOVB
|
||||
case 2:
|
||||
return s390x.AMOVH
|
||||
case 4:
|
||||
return s390x.AMOVW
|
||||
case 8:
|
||||
return s390x.AMOVD
|
||||
}
|
||||
}
|
||||
panic("bad store type")
|
||||
}
|
||||
|
||||
// moveByType returns the reg->reg move instruction of the given type.
|
||||
func moveByType(t ssa.Type) obj.As {
|
||||
if t.IsFloat() {
|
||||
return s390x.AFMOVD
|
||||
} else {
|
||||
switch t.Size() {
|
||||
case 1:
|
||||
if t.IsSigned() {
|
||||
return s390x.AMOVB
|
||||
} else {
|
||||
return s390x.AMOVBZ
|
||||
}
|
||||
case 2:
|
||||
if t.IsSigned() {
|
||||
return s390x.AMOVH
|
||||
} else {
|
||||
return s390x.AMOVHZ
|
||||
}
|
||||
case 4:
|
||||
if t.IsSigned() {
|
||||
return s390x.AMOVW
|
||||
} else {
|
||||
return s390x.AMOVWZ
|
||||
}
|
||||
case 8:
|
||||
return s390x.AMOVD
|
||||
}
|
||||
}
|
||||
panic("bad load type")
|
||||
}
|
||||
|
||||
// opregreg emits instructions for
|
||||
// dest := dest(To) op src(From)
|
||||
// and also returns the created obj.Prog so it
|
||||
// may be further adjusted (offset, scale, etc).
|
||||
func opregreg(op obj.As, dest, src int16) *obj.Prog {
|
||||
p := gc.Prog(op)
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = dest
|
||||
p.From.Reg = src
|
||||
return p
|
||||
}
|
||||
|
||||
// opregregimm emits instructions for
|
||||
// dest := src(From) op off
|
||||
// and also returns the created obj.Prog so it
|
||||
// may be further adjusted (offset, scale, etc).
|
||||
func opregregimm(op obj.As, dest, src int16, off int64) *obj.Prog {
|
||||
p := gc.Prog(op)
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
p.From.Offset = off
|
||||
p.Reg = src
|
||||
p.To.Reg = dest
|
||||
p.To.Type = obj.TYPE_REG
|
||||
return p
|
||||
}
|
||||
|
||||
func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
|
||||
s.SetLineno(v.Line)
|
||||
switch v.Op {
|
||||
case ssa.OpS390XSLD, ssa.OpS390XSLW,
|
||||
ssa.OpS390XSRD, ssa.OpS390XSRW,
|
||||
ssa.OpS390XSRAD, ssa.OpS390XSRAW:
|
||||
r := gc.SSARegNum(v)
|
||||
r1 := gc.SSARegNum(v.Args[0])
|
||||
r2 := gc.SSARegNum(v.Args[1])
|
||||
if r2 == s390x.REG_R0 {
|
||||
v.Fatalf("cannot use R0 as shift value %s", v.LongString())
|
||||
}
|
||||
p := opregreg(v.Op.Asm(), r, r2)
|
||||
if r != r1 {
|
||||
p.Reg = r1
|
||||
}
|
||||
case ssa.OpS390XADD, ssa.OpS390XADDW,
|
||||
ssa.OpS390XSUB, ssa.OpS390XSUBW,
|
||||
ssa.OpS390XAND, ssa.OpS390XANDW,
|
||||
ssa.OpS390XOR, ssa.OpS390XORW,
|
||||
ssa.OpS390XXOR, ssa.OpS390XXORW:
|
||||
r := gc.SSARegNum(v)
|
||||
r1 := gc.SSARegNum(v.Args[0])
|
||||
r2 := gc.SSARegNum(v.Args[1])
|
||||
p := opregreg(v.Op.Asm(), r, r2)
|
||||
if r != r1 {
|
||||
p.Reg = r1
|
||||
}
|
||||
// 2-address opcode arithmetic
|
||||
case ssa.OpS390XMULLD, ssa.OpS390XMULLW,
|
||||
ssa.OpS390XMULHD, ssa.OpS390XMULHDU,
|
||||
ssa.OpS390XFADDS, ssa.OpS390XFADD, ssa.OpS390XFSUBS, ssa.OpS390XFSUB,
|
||||
ssa.OpS390XFMULS, ssa.OpS390XFMUL, ssa.OpS390XFDIVS, ssa.OpS390XFDIV:
|
||||
r := gc.SSARegNum(v)
|
||||
if r != gc.SSARegNum(v.Args[0]) {
|
||||
v.Fatalf("input[0] and output not in same register %s", v.LongString())
|
||||
}
|
||||
opregreg(v.Op.Asm(), r, gc.SSARegNum(v.Args[1]))
|
||||
case ssa.OpS390XDIVD, ssa.OpS390XDIVW,
|
||||
ssa.OpS390XDIVDU, ssa.OpS390XDIVWU,
|
||||
ssa.OpS390XMODD, ssa.OpS390XMODW,
|
||||
ssa.OpS390XMODDU, ssa.OpS390XMODWU:
|
||||
|
||||
// TODO(mundaym): use the temp registers every time like x86 does with AX?
|
||||
dividend := gc.SSARegNum(v.Args[0])
|
||||
divisor := gc.SSARegNum(v.Args[1])
|
||||
|
||||
// CPU faults upon signed overflow, which occurs when most
|
||||
// negative int is divided by -1.
|
||||
var j *obj.Prog
|
||||
if v.Op == ssa.OpS390XDIVD || v.Op == ssa.OpS390XDIVW ||
|
||||
v.Op == ssa.OpS390XMODD || v.Op == ssa.OpS390XMODW {
|
||||
|
||||
var c *obj.Prog
|
||||
c = gc.Prog(s390x.ACMP)
|
||||
j = gc.Prog(s390x.ABEQ)
|
||||
|
||||
c.From.Type = obj.TYPE_REG
|
||||
c.From.Reg = divisor
|
||||
c.To.Type = obj.TYPE_CONST
|
||||
c.To.Offset = -1
|
||||
|
||||
j.To.Type = obj.TYPE_BRANCH
|
||||
|
||||
}
|
||||
|
||||
p := gc.Prog(v.Op.Asm())
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = divisor
|
||||
p.Reg = 0
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = dividend
|
||||
|
||||
// signed division, rest of the check for -1 case
|
||||
if j != nil {
|
||||
j2 := gc.Prog(s390x.ABR)
|
||||
j2.To.Type = obj.TYPE_BRANCH
|
||||
|
||||
var n *obj.Prog
|
||||
if v.Op == ssa.OpS390XDIVD || v.Op == ssa.OpS390XDIVW {
|
||||
// n * -1 = -n
|
||||
n = gc.Prog(s390x.ANEG)
|
||||
n.To.Type = obj.TYPE_REG
|
||||
n.To.Reg = dividend
|
||||
} else {
|
||||
// n % -1 == 0
|
||||
n = gc.Prog(s390x.AXOR)
|
||||
n.From.Type = obj.TYPE_REG
|
||||
n.From.Reg = dividend
|
||||
n.To.Type = obj.TYPE_REG
|
||||
n.To.Reg = dividend
|
||||
}
|
||||
|
||||
j.To.Val = n
|
||||
j2.To.Val = s.Pc()
|
||||
}
|
||||
case ssa.OpS390XADDconst, ssa.OpS390XADDWconst:
|
||||
opregregimm(v.Op.Asm(), gc.SSARegNum(v), gc.SSARegNum(v.Args[0]), v.AuxInt)
|
||||
case ssa.OpS390XMULLDconst, ssa.OpS390XMULLWconst,
|
||||
ssa.OpS390XSUBconst, ssa.OpS390XSUBWconst,
|
||||
ssa.OpS390XANDconst, ssa.OpS390XANDWconst,
|
||||
ssa.OpS390XORconst, ssa.OpS390XORWconst,
|
||||
ssa.OpS390XXORconst, ssa.OpS390XXORWconst:
|
||||
r := gc.SSARegNum(v)
|
||||
if r != gc.SSARegNum(v.Args[0]) {
|
||||
v.Fatalf("input[0] and output not in same register %s", v.LongString())
|
||||
}
|
||||
p := gc.Prog(v.Op.Asm())
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
p.From.Offset = v.AuxInt
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = r
|
||||
case ssa.OpS390XSLDconst, ssa.OpS390XSLWconst,
|
||||
ssa.OpS390XSRDconst, ssa.OpS390XSRWconst,
|
||||
ssa.OpS390XSRADconst, ssa.OpS390XSRAWconst,
|
||||
ssa.OpS390XRLLGconst, ssa.OpS390XRLLconst:
|
||||
p := gc.Prog(v.Op.Asm())
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
p.From.Offset = v.AuxInt
|
||||
r := gc.SSARegNum(v)
|
||||
r1 := gc.SSARegNum(v.Args[0])
|
||||
if r != r1 {
|
||||
p.Reg = r1
|
||||
}
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = r
|
||||
case ssa.OpS390XSUBEcarrymask, ssa.OpS390XSUBEWcarrymask:
|
||||
r := gc.SSARegNum(v)
|
||||
p := gc.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.OpS390XMOVDaddridx:
|
||||
r := gc.SSARegNum(v.Args[0])
|
||||
i := gc.SSARegNum(v.Args[1])
|
||||
p := gc.Prog(s390x.AMOVD)
|
||||
p.From.Scale = 1
|
||||
if i == s390x.REGSP {
|
||||
r, i = i, r
|
||||
}
|
||||
p.From.Type = obj.TYPE_ADDR
|
||||
p.From.Reg = r
|
||||
p.From.Index = i
|
||||
gc.AddAux(&p.From, v)
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = gc.SSARegNum(v)
|
||||
case ssa.OpS390XMOVDaddr:
|
||||
p := gc.Prog(s390x.AMOVD)
|
||||
p.From.Type = obj.TYPE_ADDR
|
||||
p.From.Reg = gc.SSARegNum(v.Args[0])
|
||||
gc.AddAux(&p.From, v)
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = gc.SSARegNum(v)
|
||||
case ssa.OpS390XCMP, ssa.OpS390XCMPW, ssa.OpS390XCMPU, ssa.OpS390XCMPWU:
|
||||
opregreg(v.Op.Asm(), gc.SSARegNum(v.Args[1]), gc.SSARegNum(v.Args[0]))
|
||||
case ssa.OpS390XTESTB:
|
||||
p := gc.Prog(v.Op.Asm())
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
p.From.Offset = 0xFF
|
||||
p.Reg = gc.SSARegNum(v.Args[0])
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = s390x.REGTMP
|
||||
case ssa.OpS390XFCMPS, ssa.OpS390XFCMP:
|
||||
opregreg(v.Op.Asm(), gc.SSARegNum(v.Args[1]), gc.SSARegNum(v.Args[0]))
|
||||
case ssa.OpS390XCMPconst, ssa.OpS390XCMPWconst, ssa.OpS390XCMPUconst, ssa.OpS390XCMPWUconst:
|
||||
p := gc.Prog(v.Op.Asm())
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = gc.SSARegNum(v.Args[0])
|
||||
p.To.Type = obj.TYPE_CONST
|
||||
p.To.Offset = v.AuxInt
|
||||
case ssa.OpS390XMOVDconst:
|
||||
x := gc.SSARegNum(v)
|
||||
p := gc.Prog(v.Op.Asm())
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
p.From.Offset = v.AuxInt
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = x
|
||||
case ssa.OpS390XFMOVSconst, ssa.OpS390XFMOVDconst:
|
||||
x := gc.SSARegNum(v)
|
||||
p := gc.Prog(v.Op.Asm())
|
||||
p.From.Type = obj.TYPE_FCONST
|
||||
p.From.Val = math.Float64frombits(uint64(v.AuxInt))
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = x
|
||||
case ssa.OpS390XMOVDload,
|
||||
ssa.OpS390XMOVWZload, ssa.OpS390XMOVHZload, ssa.OpS390XMOVBZload,
|
||||
ssa.OpS390XMOVDBRload, ssa.OpS390XMOVWBRload, ssa.OpS390XMOVHBRload,
|
||||
ssa.OpS390XMOVBload, ssa.OpS390XMOVHload, ssa.OpS390XMOVWload,
|
||||
ssa.OpS390XFMOVSload, ssa.OpS390XFMOVDload:
|
||||
p := gc.Prog(v.Op.Asm())
|
||||
p.From.Type = obj.TYPE_MEM
|
||||
p.From.Reg = gc.SSARegNum(v.Args[0])
|
||||
gc.AddAux(&p.From, v)
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = gc.SSARegNum(v)
|
||||
case ssa.OpS390XMOVBZloadidx, ssa.OpS390XMOVHZloadidx, ssa.OpS390XMOVWZloadidx, ssa.OpS390XMOVDloadidx,
|
||||
ssa.OpS390XMOVHBRloadidx, ssa.OpS390XMOVWBRloadidx, ssa.OpS390XMOVDBRloadidx,
|
||||
ssa.OpS390XFMOVSloadidx, ssa.OpS390XFMOVDloadidx:
|
||||
r := gc.SSARegNum(v.Args[0])
|
||||
i := gc.SSARegNum(v.Args[1])
|
||||
if i == s390x.REGSP {
|
||||
r, i = i, r
|
||||
}
|
||||
p := gc.Prog(v.Op.Asm())
|
||||
p.From.Type = obj.TYPE_MEM
|
||||
p.From.Reg = r
|
||||
p.From.Scale = 1
|
||||
p.From.Index = i
|
||||
gc.AddAux(&p.From, v)
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = gc.SSARegNum(v)
|
||||
case ssa.OpS390XMOVBstore, ssa.OpS390XMOVHstore, ssa.OpS390XMOVWstore, ssa.OpS390XMOVDstore,
|
||||
ssa.OpS390XFMOVSstore, ssa.OpS390XFMOVDstore:
|
||||
p := gc.Prog(v.Op.Asm())
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = gc.SSARegNum(v.Args[1])
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Reg = gc.SSARegNum(v.Args[0])
|
||||
gc.AddAux(&p.To, v)
|
||||
case ssa.OpS390XMOVBstoreidx, ssa.OpS390XMOVHstoreidx, ssa.OpS390XMOVWstoreidx, ssa.OpS390XMOVDstoreidx,
|
||||
ssa.OpS390XFMOVSstoreidx, ssa.OpS390XFMOVDstoreidx:
|
||||
r := gc.SSARegNum(v.Args[0])
|
||||
i := gc.SSARegNum(v.Args[1])
|
||||
if i == s390x.REGSP {
|
||||
r, i = i, r
|
||||
}
|
||||
p := gc.Prog(v.Op.Asm())
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = gc.SSARegNum(v.Args[2])
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Reg = r
|
||||
p.To.Scale = 1
|
||||
p.To.Index = i
|
||||
gc.AddAux(&p.To, v)
|
||||
case ssa.OpS390XMOVDstoreconst, ssa.OpS390XMOVWstoreconst, ssa.OpS390XMOVHstoreconst, ssa.OpS390XMOVBstoreconst:
|
||||
p := gc.Prog(v.Op.Asm())
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
sc := v.AuxValAndOff()
|
||||
p.From.Offset = sc.Val()
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Reg = gc.SSARegNum(v.Args[0])
|
||||
gc.AddAux2(&p.To, v, sc.Off())
|
||||
case ssa.OpS390XMOVBreg, ssa.OpS390XMOVHreg, ssa.OpS390XMOVWreg,
|
||||
ssa.OpS390XMOVBZreg, ssa.OpS390XMOVHZreg, ssa.OpS390XMOVWZreg,
|
||||
ssa.OpS390XCEFBRA, ssa.OpS390XCDFBRA, ssa.OpS390XCEGBRA, ssa.OpS390XCDGBRA,
|
||||
ssa.OpS390XCFEBRA, ssa.OpS390XCFDBRA, ssa.OpS390XCGEBRA, ssa.OpS390XCGDBRA,
|
||||
ssa.OpS390XLDEBR, ssa.OpS390XLEDBR,
|
||||
ssa.OpS390XFNEG, ssa.OpS390XFNEGS:
|
||||
opregreg(v.Op.Asm(), gc.SSARegNum(v), gc.SSARegNum(v.Args[0]))
|
||||
case ssa.OpS390XCLEAR:
|
||||
p := gc.Prog(v.Op.Asm())
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
sc := v.AuxValAndOff()
|
||||
p.From.Offset = sc.Val()
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Reg = gc.SSARegNum(v.Args[0])
|
||||
gc.AddAux2(&p.To, v, sc.Off())
|
||||
case ssa.OpCopy, ssa.OpS390XMOVDconvert:
|
||||
if v.Type.IsMemory() {
|
||||
return
|
||||
}
|
||||
x := gc.SSARegNum(v.Args[0])
|
||||
y := gc.SSARegNum(v)
|
||||
if x != y {
|
||||
opregreg(moveByType(v.Type), y, x)
|
||||
}
|
||||
case ssa.OpLoadReg:
|
||||
if v.Type.IsFlags() {
|
||||
v.Unimplementedf("load flags not implemented: %v", v.LongString())
|
||||
return
|
||||
}
|
||||
p := gc.Prog(loadByType(v.Type))
|
||||
n, off := gc.AutoVar(v.Args[0])
|
||||
p.From.Type = obj.TYPE_MEM
|
||||
p.From.Node = n
|
||||
p.From.Sym = gc.Linksym(n.Sym)
|
||||
p.From.Offset = off
|
||||
if n.Class == gc.PPARAM || n.Class == gc.PPARAMOUT {
|
||||
p.From.Name = obj.NAME_PARAM
|
||||
p.From.Offset += n.Xoffset
|
||||
} else {
|
||||
p.From.Name = obj.NAME_AUTO
|
||||
}
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = gc.SSARegNum(v)
|
||||
case ssa.OpStoreReg:
|
||||
if v.Type.IsFlags() {
|
||||
v.Unimplementedf("store flags not implemented: %v", v.LongString())
|
||||
return
|
||||
}
|
||||
p := gc.Prog(storeByType(v.Type))
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = gc.SSARegNum(v.Args[0])
|
||||
n, off := gc.AutoVar(v)
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Node = n
|
||||
p.To.Sym = gc.Linksym(n.Sym)
|
||||
p.To.Offset = off
|
||||
if n.Class == gc.PPARAM || n.Class == gc.PPARAMOUT {
|
||||
p.To.Name = obj.NAME_PARAM
|
||||
p.To.Offset += n.Xoffset
|
||||
} else {
|
||||
p.To.Name = obj.NAME_AUTO
|
||||
}
|
||||
case ssa.OpPhi:
|
||||
gc.CheckLoweredPhi(v)
|
||||
case ssa.OpInitMem:
|
||||
// memory arg needs no code
|
||||
case ssa.OpArg:
|
||||
// input args need no code
|
||||
case ssa.OpS390XLoweredGetClosurePtr:
|
||||
// Closure pointer is R12 (already)
|
||||
gc.CheckLoweredGetClosurePtr(v)
|
||||
case ssa.OpS390XLoweredGetG:
|
||||
r := gc.SSARegNum(v)
|
||||
p := gc.Prog(s390x.AMOVD)
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = s390x.REGG
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = r
|
||||
case ssa.OpS390XCALLstatic:
|
||||
if v.Aux.(*gc.Sym) == gc.Deferreturn.Sym {
|
||||
// Deferred calls will appear to be returning to
|
||||
// the CALL deferreturn(SB) that we are about to emit.
|
||||
// However, the stack trace code will show the line
|
||||
// of the instruction byte before the return PC.
|
||||
// To avoid that being an unrelated instruction,
|
||||
// insert an actual hardware NOP that will have the right line number.
|
||||
// This is different from obj.ANOP, which is a virtual no-op
|
||||
// that doesn't make it into the instruction stream.
|
||||
ginsnop()
|
||||
}
|
||||
p := gc.Prog(obj.ACALL)
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_EXTERN
|
||||
p.To.Sym = gc.Linksym(v.Aux.(*gc.Sym))
|
||||
if gc.Maxarg < v.AuxInt {
|
||||
gc.Maxarg = v.AuxInt
|
||||
}
|
||||
case ssa.OpS390XCALLclosure:
|
||||
p := gc.Prog(obj.ACALL)
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = gc.SSARegNum(v.Args[0])
|
||||
if gc.Maxarg < v.AuxInt {
|
||||
gc.Maxarg = v.AuxInt
|
||||
}
|
||||
case ssa.OpS390XCALLdefer:
|
||||
p := gc.Prog(obj.ACALL)
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_EXTERN
|
||||
p.To.Sym = gc.Linksym(gc.Deferproc.Sym)
|
||||
if gc.Maxarg < v.AuxInt {
|
||||
gc.Maxarg = v.AuxInt
|
||||
}
|
||||
case ssa.OpS390XCALLgo:
|
||||
p := gc.Prog(obj.ACALL)
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_EXTERN
|
||||
p.To.Sym = gc.Linksym(gc.Newproc.Sym)
|
||||
if gc.Maxarg < v.AuxInt {
|
||||
gc.Maxarg = v.AuxInt
|
||||
}
|
||||
case ssa.OpS390XCALLinter:
|
||||
p := gc.Prog(obj.ACALL)
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = gc.SSARegNum(v.Args[0])
|
||||
if gc.Maxarg < v.AuxInt {
|
||||
gc.Maxarg = v.AuxInt
|
||||
}
|
||||
case ssa.OpS390XNEG, ssa.OpS390XNEGW:
|
||||
r := gc.SSARegNum(v)
|
||||
p := gc.Prog(v.Op.Asm())
|
||||
r1 := gc.SSARegNum(v.Args[0])
|
||||
if r != r1 {
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = r1
|
||||
}
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = r
|
||||
case ssa.OpS390XNOT, ssa.OpS390XNOTW:
|
||||
v.Fatalf("NOT/NOTW generated %s", v.LongString())
|
||||
case ssa.OpS390XMOVDEQ, ssa.OpS390XMOVDNE,
|
||||
ssa.OpS390XMOVDLT, ssa.OpS390XMOVDLE,
|
||||
ssa.OpS390XMOVDGT, ssa.OpS390XMOVDGE,
|
||||
ssa.OpS390XMOVDGTnoinv, ssa.OpS390XMOVDGEnoinv:
|
||||
r := gc.SSARegNum(v)
|
||||
if r != gc.SSARegNum(v.Args[0]) {
|
||||
v.Fatalf("input[0] and output not in same register %s", v.LongString())
|
||||
}
|
||||
p := gc.Prog(v.Op.Asm())
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = gc.SSARegNum(v.Args[1])
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = r
|
||||
case ssa.OpS390XFSQRT:
|
||||
p := gc.Prog(v.Op.Asm())
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = gc.SSARegNum(v.Args[0])
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = gc.SSARegNum(v)
|
||||
case ssa.OpSP, ssa.OpSB:
|
||||
// nothing to do
|
||||
case ssa.OpVarDef:
|
||||
gc.Gvardef(v.Aux.(*gc.Node))
|
||||
case ssa.OpVarKill:
|
||||
gc.Gvarkill(v.Aux.(*gc.Node))
|
||||
case ssa.OpVarLive:
|
||||
gc.Gvarlive(v.Aux.(*gc.Node))
|
||||
case ssa.OpKeepAlive:
|
||||
gc.KeepAlive(v)
|
||||
case ssa.OpS390XInvertFlags:
|
||||
v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
|
||||
case ssa.OpS390XFlagEQ, ssa.OpS390XFlagLT, ssa.OpS390XFlagGT:
|
||||
v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString())
|
||||
case ssa.OpS390XLoweredNilCheck:
|
||||
// Optimization - if the subsequent block has a load or store
|
||||
// at the same address, we don't need to issue this instruction.
|
||||
mem := v.Args[1]
|
||||
for _, w := range v.Block.Succs[0].Block().Values {
|
||||
if w.Op == ssa.OpPhi {
|
||||
if w.Type.IsMemory() {
|
||||
mem = w
|
||||
}
|
||||
continue
|
||||
}
|
||||
if len(w.Args) == 0 || !w.Args[len(w.Args)-1].Type.IsMemory() {
|
||||
// w doesn't use a store - can't be a memory op.
|
||||
continue
|
||||
}
|
||||
if w.Args[len(w.Args)-1] != mem {
|
||||
v.Fatalf("wrong store after nilcheck v=%s w=%s", v, w)
|
||||
}
|
||||
switch w.Op {
|
||||
case ssa.OpS390XMOVDload,
|
||||
ssa.OpS390XMOVBload, ssa.OpS390XMOVHload, ssa.OpS390XMOVWload,
|
||||
ssa.OpS390XMOVBZload, ssa.OpS390XMOVHZload, ssa.OpS390XMOVWZload,
|
||||
ssa.OpS390XMOVHBRload, ssa.OpS390XMOVWBRload, ssa.OpS390XMOVDBRload,
|
||||
ssa.OpS390XMOVBstore, ssa.OpS390XMOVHstore, ssa.OpS390XMOVWstore, ssa.OpS390XMOVDstore,
|
||||
ssa.OpS390XFMOVSload, ssa.OpS390XFMOVDload,
|
||||
ssa.OpS390XFMOVSstore, ssa.OpS390XFMOVDstore,
|
||||
ssa.OpS390XSTMG2, ssa.OpS390XSTMG3, ssa.OpS390XSTMG4,
|
||||
ssa.OpS390XSTM2, ssa.OpS390XSTM3, ssa.OpS390XSTM4:
|
||||
if w.Args[0] == v.Args[0] && w.Aux == nil && w.AuxInt >= 0 && w.AuxInt < minZeroPage {
|
||||
if gc.Debug_checknil != 0 && int(v.Line) > 1 {
|
||||
gc.Warnl(v.Line, "removed nil check")
|
||||
}
|
||||
return
|
||||
}
|
||||
case ssa.OpS390XMOVDstoreconst, ssa.OpS390XMOVWstoreconst, ssa.OpS390XMOVHstoreconst, ssa.OpS390XMOVBstoreconst,
|
||||
ssa.OpS390XCLEAR:
|
||||
off := ssa.ValAndOff(v.AuxInt).Off()
|
||||
if w.Args[0] == v.Args[0] && w.Aux == nil && off >= 0 && off < minZeroPage {
|
||||
if gc.Debug_checknil != 0 && int(v.Line) > 1 {
|
||||
gc.Warnl(v.Line, "removed nil check")
|
||||
}
|
||||
return
|
||||
}
|
||||
case ssa.OpS390XMVC:
|
||||
off := ssa.ValAndOff(v.AuxInt).Off()
|
||||
if (w.Args[0] == v.Args[0] || w.Args[1] == v.Args[0]) && w.Aux == nil && off >= 0 && off < minZeroPage {
|
||||
if gc.Debug_checknil != 0 && int(v.Line) > 1 {
|
||||
gc.Warnl(v.Line, "removed nil check")
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
if w.Type.IsMemory() {
|
||||
if w.Op == ssa.OpVarDef || w.Op == ssa.OpVarKill || w.Op == ssa.OpVarLive {
|
||||
// these ops are OK
|
||||
mem = w
|
||||
continue
|
||||
}
|
||||
// We can't delay the nil check past the next store.
|
||||
break
|
||||
}
|
||||
}
|
||||
// Issue a load which will fault if the input is nil.
|
||||
p := gc.Prog(s390x.AMOVBZ)
|
||||
p.From.Type = obj.TYPE_MEM
|
||||
p.From.Reg = gc.SSARegNum(v.Args[0])
|
||||
gc.AddAux(&p.From, v)
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = s390x.REGTMP
|
||||
if gc.Debug_checknil != 0 && v.Line > 1 { // v.Line==1 in generated wrappers
|
||||
gc.Warnl(v.Line, "generated nil check")
|
||||
}
|
||||
case ssa.OpS390XMVC:
|
||||
vo := v.AuxValAndOff()
|
||||
p := gc.Prog(s390x.AMVC)
|
||||
p.From.Type = obj.TYPE_MEM
|
||||
p.From.Reg = gc.SSARegNum(v.Args[1])
|
||||
p.From.Offset = vo.Off()
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Reg = gc.SSARegNum(v.Args[0])
|
||||
p.To.Offset = vo.Off()
|
||||
p.From3 = new(obj.Addr)
|
||||
p.From3.Type = obj.TYPE_CONST
|
||||
p.From3.Offset = vo.Val()
|
||||
case ssa.OpS390XSTMG2, ssa.OpS390XSTMG3, ssa.OpS390XSTMG4,
|
||||
ssa.OpS390XSTM2, ssa.OpS390XSTM3, ssa.OpS390XSTM4:
|
||||
for i := 2; i < len(v.Args)-1; i++ {
|
||||
if gc.SSARegNum(v.Args[i]) != gc.SSARegNum(v.Args[i-1])+1 {
|
||||
v.Fatalf("invalid store multiple %s", v.LongString())
|
||||
}
|
||||
}
|
||||
p := gc.Prog(v.Op.Asm())
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = gc.SSARegNum(v.Args[1])
|
||||
p.Reg = gc.SSARegNum(v.Args[len(v.Args)-2])
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Reg = gc.SSARegNum(v.Args[0])
|
||||
gc.AddAux(&p.To, v)
|
||||
case ssa.OpS390XLoweredMove:
|
||||
// Inputs must be valid pointers to memory,
|
||||
// so adjust arg0 and arg1 as part of the expansion.
|
||||
// arg2 should be src+size,
|
||||
//
|
||||
// mvc: MVC $256, 0(R2), 0(R1)
|
||||
// MOVD $256(R1), R1
|
||||
// MOVD $256(R2), R2
|
||||
// CMP R2, Rarg2
|
||||
// BNE mvc
|
||||
// MVC $rem, 0(R2), 0(R1) // if rem > 0
|
||||
// arg2 is the last address to move in the loop + 256
|
||||
mvc := gc.Prog(s390x.AMVC)
|
||||
mvc.From.Type = obj.TYPE_MEM
|
||||
mvc.From.Reg = gc.SSARegNum(v.Args[1])
|
||||
mvc.To.Type = obj.TYPE_MEM
|
||||
mvc.To.Reg = gc.SSARegNum(v.Args[0])
|
||||
mvc.From3 = new(obj.Addr)
|
||||
mvc.From3.Type = obj.TYPE_CONST
|
||||
mvc.From3.Offset = 256
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
movd := gc.Prog(s390x.AMOVD)
|
||||
movd.From.Type = obj.TYPE_ADDR
|
||||
movd.From.Reg = gc.SSARegNum(v.Args[i])
|
||||
movd.From.Offset = 256
|
||||
movd.To.Type = obj.TYPE_REG
|
||||
movd.To.Reg = gc.SSARegNum(v.Args[i])
|
||||
}
|
||||
|
||||
cmpu := gc.Prog(s390x.ACMPU)
|
||||
cmpu.From.Reg = gc.SSARegNum(v.Args[1])
|
||||
cmpu.From.Type = obj.TYPE_REG
|
||||
cmpu.To.Reg = gc.SSARegNum(v.Args[2])
|
||||
cmpu.To.Type = obj.TYPE_REG
|
||||
|
||||
bne := gc.Prog(s390x.ABLT)
|
||||
bne.To.Type = obj.TYPE_BRANCH
|
||||
gc.Patch(bne, mvc)
|
||||
|
||||
if v.AuxInt > 0 {
|
||||
mvc := gc.Prog(s390x.AMVC)
|
||||
mvc.From.Type = obj.TYPE_MEM
|
||||
mvc.From.Reg = gc.SSARegNum(v.Args[1])
|
||||
mvc.To.Type = obj.TYPE_MEM
|
||||
mvc.To.Reg = gc.SSARegNum(v.Args[0])
|
||||
mvc.From3 = new(obj.Addr)
|
||||
mvc.From3.Type = obj.TYPE_CONST
|
||||
mvc.From3.Offset = v.AuxInt
|
||||
}
|
||||
case ssa.OpS390XLoweredZero:
|
||||
// Input must be valid pointers to memory,
|
||||
// so adjust arg0 as part of the expansion.
|
||||
// arg1 should be src+size,
|
||||
//
|
||||
// clear: CLEAR $256, 0(R1)
|
||||
// MOVD $256(R1), R1
|
||||
// CMP R1, Rarg1
|
||||
// BNE clear
|
||||
// CLEAR $rem, 0(R1) // if rem > 0
|
||||
// arg1 is the last address to zero in the loop + 256
|
||||
clear := gc.Prog(s390x.ACLEAR)
|
||||
clear.From.Type = obj.TYPE_CONST
|
||||
clear.From.Offset = 256
|
||||
clear.To.Type = obj.TYPE_MEM
|
||||
clear.To.Reg = gc.SSARegNum(v.Args[0])
|
||||
|
||||
movd := gc.Prog(s390x.AMOVD)
|
||||
movd.From.Type = obj.TYPE_ADDR
|
||||
movd.From.Reg = gc.SSARegNum(v.Args[0])
|
||||
movd.From.Offset = 256
|
||||
movd.To.Type = obj.TYPE_REG
|
||||
movd.To.Reg = gc.SSARegNum(v.Args[0])
|
||||
|
||||
cmpu := gc.Prog(s390x.ACMPU)
|
||||
cmpu.From.Reg = gc.SSARegNum(v.Args[0])
|
||||
cmpu.From.Type = obj.TYPE_REG
|
||||
cmpu.To.Reg = gc.SSARegNum(v.Args[1])
|
||||
cmpu.To.Type = obj.TYPE_REG
|
||||
|
||||
bne := gc.Prog(s390x.ABLT)
|
||||
bne.To.Type = obj.TYPE_BRANCH
|
||||
gc.Patch(bne, clear)
|
||||
|
||||
if v.AuxInt > 0 {
|
||||
clear := gc.Prog(s390x.ACLEAR)
|
||||
clear.From.Type = obj.TYPE_CONST
|
||||
clear.From.Offset = v.AuxInt
|
||||
clear.To.Type = obj.TYPE_MEM
|
||||
clear.To.Reg = gc.SSARegNum(v.Args[0])
|
||||
}
|
||||
default:
|
||||
v.Unimplementedf("genValue not implemented: %s", v.LongString())
|
||||
}
|
||||
}
|
||||
|
||||
var blockJump = [...]struct {
|
||||
asm, invasm obj.As
|
||||
}{
|
||||
ssa.BlockS390XEQ: {s390x.ABEQ, s390x.ABNE},
|
||||
ssa.BlockS390XNE: {s390x.ABNE, s390x.ABEQ},
|
||||
ssa.BlockS390XLT: {s390x.ABLT, s390x.ABGE},
|
||||
ssa.BlockS390XGE: {s390x.ABGE, s390x.ABLT},
|
||||
ssa.BlockS390XLE: {s390x.ABLE, s390x.ABGT},
|
||||
ssa.BlockS390XGT: {s390x.ABGT, s390x.ABLE},
|
||||
ssa.BlockS390XGTF: {s390x.ABGT, s390x.ABLEU},
|
||||
ssa.BlockS390XGEF: {s390x.ABGE, s390x.ABLTU},
|
||||
}
|
||||
|
||||
func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
|
||||
s.SetLineno(b.Line)
|
||||
|
||||
switch b.Kind {
|
||||
case ssa.BlockPlain, ssa.BlockCheck:
|
||||
if b.Succs[0].Block() != next {
|
||||
p := gc.Prog(s390x.ABR)
|
||||
p.To.Type = obj.TYPE_BRANCH
|
||||
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
|
||||
}
|
||||
case ssa.BlockDefer:
|
||||
// defer returns in R3:
|
||||
// 0 if we should continue executing
|
||||
// 1 if we should jump to deferreturn call
|
||||
p := gc.Prog(s390x.AAND)
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
p.From.Offset = 0xFFFFFFFF
|
||||
p.Reg = s390x.REG_R3
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = s390x.REG_R3
|
||||
p = gc.Prog(s390x.ABNE)
|
||||
p.To.Type = obj.TYPE_BRANCH
|
||||
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
|
||||
if b.Succs[0].Block() != next {
|
||||
p := gc.Prog(s390x.ABR)
|
||||
p.To.Type = obj.TYPE_BRANCH
|
||||
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
|
||||
}
|
||||
case ssa.BlockExit:
|
||||
gc.Prog(obj.AUNDEF) // tell plive.go that we never reach here
|
||||
case ssa.BlockRet:
|
||||
gc.Prog(obj.ARET)
|
||||
case ssa.BlockRetJmp:
|
||||
p := gc.Prog(s390x.ABR)
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_EXTERN
|
||||
p.To.Sym = gc.Linksym(b.Aux.(*gc.Sym))
|
||||
case ssa.BlockS390XEQ, ssa.BlockS390XNE,
|
||||
ssa.BlockS390XLT, ssa.BlockS390XGE,
|
||||
ssa.BlockS390XLE, ssa.BlockS390XGT,
|
||||
ssa.BlockS390XGEF, ssa.BlockS390XGTF:
|
||||
jmp := blockJump[b.Kind]
|
||||
likely := b.Likely
|
||||
var p *obj.Prog
|
||||
switch next {
|
||||
case b.Succs[0].Block():
|
||||
p = gc.Prog(jmp.invasm)
|
||||
likely *= -1
|
||||
p.To.Type = obj.TYPE_BRANCH
|
||||
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
|
||||
case b.Succs[1].Block():
|
||||
p = gc.Prog(jmp.asm)
|
||||
p.To.Type = obj.TYPE_BRANCH
|
||||
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
|
||||
default:
|
||||
p = gc.Prog(jmp.asm)
|
||||
p.To.Type = obj.TYPE_BRANCH
|
||||
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
|
||||
q := gc.Prog(s390x.ABR)
|
||||
q.To.Type = obj.TYPE_BRANCH
|
||||
s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()})
|
||||
}
|
||||
default:
|
||||
b.Unimplementedf("branch not implemented: %s. Control: %s", b.LongString(), b.Control.LongString())
|
||||
}
|
||||
}
|
@ -206,6 +206,17 @@ func NewConfig(arch string, fe Frontend, ctxt *obj.Link, optimize bool) *Config
|
||||
c.specialRegMask = specialRegMaskMIPS64
|
||||
c.FPReg = framepointerRegMIPS64
|
||||
c.hasGReg = true
|
||||
case "s390x":
|
||||
c.IntSize = 8
|
||||
c.PtrSize = 8
|
||||
c.lowerBlock = rewriteBlockS390X
|
||||
c.lowerValue = rewriteValueS390X
|
||||
c.registers = registersS390X[:]
|
||||
c.gpRegMask = gpRegMaskS390X
|
||||
c.fpRegMask = fpRegMaskS390X
|
||||
c.FPReg = framepointerRegS390X
|
||||
c.hasGReg = true
|
||||
c.noDuffDevice = true
|
||||
default:
|
||||
fe.Unimplementedf(0, "arch %s not implemented", arch)
|
||||
}
|
||||
|
1455
src/cmd/compile/internal/ssa/gen/S390X.rules
Normal file
1455
src/cmd/compile/internal/ssa/gen/S390X.rules
Normal file
File diff suppressed because it is too large
Load Diff
527
src/cmd/compile/internal/ssa/gen/S390XOps.go
Normal file
527
src/cmd/compile/internal/ssa/gen/S390XOps.go
Normal file
@ -0,0 +1,527 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import "strings"
|
||||
|
||||
// Notes:
|
||||
// - Integer types live in the low portion of registers. Upper portions are junk.
|
||||
// - Boolean types use the low-order byte of a register. 0=false, 1=true.
|
||||
// Upper bytes are junk.
|
||||
// - When doing sub-register operations, we try to write the whole
|
||||
// destination register to avoid a partial-register write.
|
||||
// - Unused portions of AuxInt (or the Val portion of ValAndOff) are
|
||||
// filled by sign-extending the used portion. Users of AuxInt which interpret
|
||||
// AuxInt as unsigned (e.g. shifts) must be careful.
|
||||
|
||||
// Suffixes encode the bit width of various instructions.
|
||||
// D (double word) = 64 bit (frequently omitted)
|
||||
// W (word) = 32 bit
|
||||
// H (half word) = 16 bit
|
||||
// B (byte) = 8 bit
|
||||
|
||||
// copied from ../../s390x/reg.go
|
||||
var regNamesS390X = []string{
|
||||
"R0",
|
||||
"R1",
|
||||
"R2",
|
||||
"R3",
|
||||
"R4",
|
||||
"R5",
|
||||
"R6",
|
||||
"R7",
|
||||
"R8",
|
||||
"R9",
|
||||
"R10",
|
||||
"R11",
|
||||
"R12",
|
||||
"g", // R13
|
||||
"R14",
|
||||
"SP", // R15
|
||||
"F0",
|
||||
"F1",
|
||||
"F2",
|
||||
"F3",
|
||||
"F4",
|
||||
"F5",
|
||||
"F6",
|
||||
"F7",
|
||||
"F8",
|
||||
"F9",
|
||||
"F10",
|
||||
"F11",
|
||||
"F12",
|
||||
"F13",
|
||||
"F14",
|
||||
"F15",
|
||||
|
||||
//pseudo-registers
|
||||
"SB",
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Make map from reg names to reg integers.
|
||||
if len(regNamesS390X) > 64 {
|
||||
panic("too many registers")
|
||||
}
|
||||
num := map[string]int{}
|
||||
for i, name := range regNamesS390X {
|
||||
num[name] = i
|
||||
}
|
||||
buildReg := func(s string) regMask {
|
||||
m := regMask(0)
|
||||
for _, r := range strings.Split(s, " ") {
|
||||
if n, ok := num[r]; ok {
|
||||
m |= regMask(1) << uint(n)
|
||||
continue
|
||||
}
|
||||
panic("register " + r + " not found")
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Common individual register masks
|
||||
var (
|
||||
sp = buildReg("SP")
|
||||
sb = buildReg("SB")
|
||||
r0 = buildReg("R0")
|
||||
|
||||
// R10 and R11 are reserved by the assembler.
|
||||
gp = buildReg("R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12")
|
||||
gpsp = gp | sp
|
||||
|
||||
// R0 is considered to contain the value 0 in address calculations.
|
||||
ptr = gp &^ r0
|
||||
ptrsp = ptr | sp
|
||||
ptrspsb = ptrsp | sb
|
||||
|
||||
fp = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15")
|
||||
callerSave = gp | fp
|
||||
)
|
||||
// Common slices of register masks
|
||||
var (
|
||||
gponly = []regMask{gp}
|
||||
fponly = []regMask{fp}
|
||||
)
|
||||
|
||||
// Common regInfo
|
||||
var (
|
||||
gp01 = regInfo{inputs: []regMask{}, outputs: gponly}
|
||||
gp11 = regInfo{inputs: []regMask{gp}, outputs: gponly}
|
||||
gp11sp = regInfo{inputs: []regMask{gpsp}, outputs: gponly}
|
||||
gp21 = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
|
||||
gp21sp = regInfo{inputs: []regMask{gpsp, gp}, outputs: gponly}
|
||||
|
||||
// R0 evaluates to 0 when used as the number of bits to shift
|
||||
// so we need to exclude it from that operand.
|
||||
sh21 = regInfo{inputs: []regMask{gp, ptr}, outputs: gponly}
|
||||
|
||||
addr = regInfo{inputs: []regMask{sp | sb}, outputs: gponly}
|
||||
addridx = regInfo{inputs: []regMask{sp | sb, ptrsp}, outputs: gponly}
|
||||
|
||||
gp2flags = regInfo{inputs: []regMask{gpsp, gpsp}}
|
||||
gp1flags = regInfo{inputs: []regMask{gpsp}}
|
||||
flagsgp = regInfo{outputs: gponly}
|
||||
gp2flags1 = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
|
||||
|
||||
gpload = regInfo{inputs: []regMask{ptrspsb, 0}, outputs: gponly}
|
||||
gploadidx = regInfo{inputs: []regMask{ptrspsb, ptrsp, 0}, outputs: gponly}
|
||||
gpstore = regInfo{inputs: []regMask{ptrspsb, gpsp, 0}}
|
||||
gpstoreconst = regInfo{inputs: []regMask{ptrspsb, 0}}
|
||||
gpstoreidx = regInfo{inputs: []regMask{ptrsp, ptrsp, gpsp, 0}}
|
||||
|
||||
gpmvc = regInfo{inputs: []regMask{ptrsp, ptrsp, 0}}
|
||||
|
||||
fp01 = regInfo{inputs: []regMask{}, outputs: fponly}
|
||||
fp21 = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
|
||||
fp21clobber = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
|
||||
fpgp = regInfo{inputs: fponly, outputs: gponly}
|
||||
gpfp = regInfo{inputs: gponly, outputs: fponly}
|
||||
fp11 = regInfo{inputs: fponly, outputs: fponly}
|
||||
fp11clobber = regInfo{inputs: fponly, outputs: fponly}
|
||||
fp2flags = regInfo{inputs: []regMask{fp, fp}}
|
||||
|
||||
fpload = regInfo{inputs: []regMask{ptrspsb, 0}, outputs: fponly}
|
||||
fploadidx = regInfo{inputs: []regMask{ptrsp, ptrsp, 0}, outputs: fponly}
|
||||
|
||||
fpstore = regInfo{inputs: []regMask{ptrspsb, fp, 0}}
|
||||
fpstoreidx = regInfo{inputs: []regMask{ptrsp, ptrsp, fp, 0}}
|
||||
)
|
||||
|
||||
var S390Xops = []opData{
|
||||
// fp ops
|
||||
{name: "FADDS", argLength: 2, reg: fp21clobber, asm: "FADDS", commutative: true, resultInArg0: true, clobberFlags: true}, // fp32 add
|
||||
{name: "FADD", argLength: 2, reg: fp21clobber, asm: "FADD", commutative: true, resultInArg0: true, clobberFlags: true}, // fp64 add
|
||||
{name: "FSUBS", argLength: 2, reg: fp21clobber, asm: "FSUBS", resultInArg0: true, clobberFlags: true}, // fp32 sub
|
||||
{name: "FSUB", argLength: 2, reg: fp21clobber, asm: "FSUB", resultInArg0: true, clobberFlags: true}, // fp64 sub
|
||||
{name: "FMULS", argLength: 2, reg: fp21, asm: "FMULS", commutative: true, resultInArg0: true}, // fp32 mul
|
||||
{name: "FMUL", argLength: 2, reg: fp21, asm: "FMUL", commutative: true, resultInArg0: true}, // fp64 mul
|
||||
{name: "FDIVS", argLength: 2, reg: fp21, asm: "FDIVS", resultInArg0: true}, // fp32 div
|
||||
{name: "FDIV", argLength: 2, reg: fp21, asm: "FDIV", resultInArg0: true}, // fp64 div
|
||||
{name: "FNEGS", argLength: 1, reg: fp11clobber, asm: "FNEGS", clobberFlags: true}, // fp32 neg
|
||||
{name: "FNEG", argLength: 1, reg: fp11clobber, asm: "FNEG", clobberFlags: true}, // fp64 neg
|
||||
|
||||
{name: "FMOVSload", argLength: 2, reg: fpload, asm: "FMOVS", aux: "SymOff"}, // fp32 load
|
||||
{name: "FMOVDload", argLength: 2, reg: fpload, asm: "FMOVD", aux: "SymOff"}, // fp64 load
|
||||
{name: "FMOVSconst", reg: fp01, asm: "FMOVS", aux: "Float32", rematerializeable: true}, // fp32 constant
|
||||
{name: "FMOVDconst", reg: fp01, asm: "FMOVD", aux: "Float64", rematerializeable: true}, // fp64 constant
|
||||
{name: "FMOVSloadidx", argLength: 3, reg: fploadidx, asm: "FMOVS", aux: "SymOff"}, // fp32 load indexed by i
|
||||
{name: "FMOVDloadidx", argLength: 3, reg: fploadidx, asm: "FMOVD", aux: "SymOff"}, // fp64 load indexed by i
|
||||
|
||||
{name: "FMOVSstore", argLength: 3, reg: fpstore, asm: "FMOVS", aux: "SymOff"}, // fp32 store
|
||||
{name: "FMOVDstore", argLength: 3, reg: fpstore, asm: "FMOVD", aux: "SymOff"}, // fp64 store
|
||||
{name: "FMOVSstoreidx", argLength: 4, reg: fpstoreidx, asm: "FMOVS", aux: "SymOff"}, // fp32 indexed by i store
|
||||
{name: "FMOVDstoreidx", argLength: 4, reg: fpstoreidx, asm: "FMOVD", aux: "SymOff"}, // fp64 indexed by i store
|
||||
|
||||
// binary ops
|
||||
{name: "ADD", argLength: 2, reg: gp21sp, asm: "ADD", commutative: true, clobberFlags: true}, // arg0 + arg1
|
||||
{name: "ADDW", argLength: 2, reg: gp21sp, asm: "ADDW", commutative: true, clobberFlags: true}, // arg0 + arg1
|
||||
{name: "ADDconst", argLength: 1, reg: gp11sp, asm: "ADD", aux: "Int64", typ: "UInt64", clobberFlags: true}, // arg0 + auxint
|
||||
{name: "ADDWconst", argLength: 1, reg: gp11sp, asm: "ADDW", aux: "Int32", clobberFlags: true}, // arg0 + auxint
|
||||
|
||||
{name: "SUB", argLength: 2, reg: gp21, asm: "SUB", clobberFlags: true}, // arg0 - arg1
|
||||
{name: "SUBW", argLength: 2, reg: gp21, asm: "SUBW", clobberFlags: true}, // arg0 - arg1
|
||||
{name: "SUBconst", argLength: 1, reg: gp11, asm: "SUB", aux: "Int64", resultInArg0: true, clobberFlags: true}, // arg0 - auxint
|
||||
{name: "SUBWconst", argLength: 1, reg: gp11, asm: "SUBW", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0 - auxint
|
||||
|
||||
{name: "MULLD", argLength: 2, reg: gp21, asm: "MULLD", typ: "Int64", commutative: true, resultInArg0: true, clobberFlags: true}, // arg0 * arg1
|
||||
{name: "MULLW", argLength: 2, reg: gp21, asm: "MULLW", typ: "Int32", commutative: true, resultInArg0: true, clobberFlags: true}, // arg0 * arg1
|
||||
{name: "MULLDconst", argLength: 1, reg: gp11, asm: "MULLD", aux: "Int64", typ: "Int64", resultInArg0: true, clobberFlags: true}, // arg0 * auxint
|
||||
{name: "MULLWconst", argLength: 1, reg: gp11, asm: "MULLW", aux: "Int32", typ: "Int32", resultInArg0: true, clobberFlags: true}, // arg0 * auxint
|
||||
|
||||
{name: "MULHD", argLength: 2, reg: gp21, asm: "MULHD", typ: "Int64", resultInArg0: true, clobberFlags: true}, // (arg0 * arg1) >> width
|
||||
{name: "MULHDU", argLength: 2, reg: gp21, asm: "MULHDU", typ: "Int64", resultInArg0: true, clobberFlags: true}, // (arg0 * arg1) >> width
|
||||
|
||||
{name: "DIVD", argLength: 2, reg: gp21, asm: "DIVD", resultInArg0: true, clobberFlags: true}, // arg0 / arg1
|
||||
{name: "DIVW", argLength: 2, reg: gp21, asm: "DIVW", resultInArg0: true, clobberFlags: true}, // arg0 / arg1
|
||||
{name: "DIVDU", argLength: 2, reg: gp21, asm: "DIVDU", resultInArg0: true, clobberFlags: true}, // arg0 / arg1
|
||||
{name: "DIVWU", argLength: 2, reg: gp21, asm: "DIVWU", resultInArg0: true, clobberFlags: true}, // arg0 / arg1
|
||||
|
||||
{name: "MODD", argLength: 2, reg: gp21, asm: "MODD", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
|
||||
{name: "MODW", argLength: 2, reg: gp21, asm: "MODW", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
|
||||
|
||||
{name: "MODDU", argLength: 2, reg: gp21, asm: "MODDU", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
|
||||
{name: "MODWU", argLength: 2, reg: gp21, asm: "MODWU", resultInArg0: 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: "AND", commutative: true, clobberFlags: true}, // arg0 & arg1
|
||||
{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64", resultInArg0: true, clobberFlags: true}, // arg0 & auxint
|
||||
{name: "ANDWconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0 & auxint
|
||||
|
||||
{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true, clobberFlags: true}, // arg0 | arg1
|
||||
{name: "ORW", argLength: 2, reg: gp21, asm: "OR", commutative: true, clobberFlags: true}, // arg0 | arg1
|
||||
{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64", resultInArg0: true, clobberFlags: true}, // arg0 | auxint
|
||||
{name: "ORWconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0 | auxint
|
||||
|
||||
{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, clobberFlags: true}, // arg0 ^ arg1
|
||||
{name: "XORW", argLength: 2, reg: gp21, asm: "XOR", commutative: true, clobberFlags: true}, // arg0 ^ arg1
|
||||
{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64", resultInArg0: true, clobberFlags: true}, // arg0 ^ auxint
|
||||
{name: "XORWconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0 ^ auxint
|
||||
|
||||
{name: "CMP", argLength: 2, reg: gp2flags, asm: "CMP", typ: "Flags"}, // arg0 compare to arg1
|
||||
{name: "CMPW", argLength: 2, reg: gp2flags, asm: "CMPW", typ: "Flags"}, // arg0 compare to arg1
|
||||
|
||||
{name: "CMPU", argLength: 2, reg: gp2flags, asm: "CMPU", typ: "Flags"}, // arg0 compare to arg1
|
||||
{name: "CMPWU", argLength: 2, reg: gp2flags, asm: "CMPWU", typ: "Flags"}, // arg0 compare to arg1
|
||||
|
||||
{name: "CMPconst", argLength: 1, reg: gp1flags, asm: "CMP", typ: "Flags", aux: "Int64"}, // arg0 compare to auxint
|
||||
{name: "CMPWconst", argLength: 1, reg: gp1flags, asm: "CMPW", typ: "Flags", aux: "Int32"}, // arg0 compare to auxint
|
||||
{name: "CMPUconst", argLength: 1, reg: gp1flags, asm: "CMPU", typ: "Flags", aux: "Int64"}, // arg0 compare to auxint
|
||||
{name: "CMPWUconst", argLength: 1, reg: gp1flags, asm: "CMPWU", typ: "Flags", aux: "Int32"}, // arg0 compare to auxint
|
||||
|
||||
{name: "FCMPS", argLength: 2, reg: fp2flags, asm: "CEBR", typ: "Flags"}, // arg0 compare to arg1, f32
|
||||
{name: "FCMP", argLength: 2, reg: fp2flags, asm: "FCMPU", typ: "Flags"}, // arg0 compare to arg1, f64
|
||||
|
||||
{name: "TESTB", argLength: 1, reg: gp1flags, asm: "AND", typ: "Flags"}, // (arg0 & 0xFF) compare to 0
|
||||
|
||||
{name: "SLD", argLength: 2, reg: sh21, asm: "SLD"}, // arg0 << arg1, shift amount is mod 64
|
||||
{name: "SLW", argLength: 2, reg: sh21, asm: "SLW"}, // arg0 << arg1, shift amount is mod 32
|
||||
{name: "SLDconst", argLength: 1, reg: gp11, asm: "SLD", aux: "Int64"}, // arg0 << auxint, shift amount 0-63
|
||||
{name: "SLWconst", argLength: 1, reg: gp11, asm: "SLW", aux: "Int32"}, // arg0 << auxint, shift amount 0-31
|
||||
|
||||
{name: "SRD", argLength: 2, reg: sh21, asm: "SRD"}, // unsigned arg0 >> arg1, shift amount is mod 64
|
||||
{name: "SRW", argLength: 2, reg: sh21, asm: "SRW"}, // unsigned arg0 >> arg1, shift amount is mod 32
|
||||
{name: "SRDconst", argLength: 1, reg: gp11, asm: "SRD", aux: "Int64"}, // unsigned arg0 >> auxint, shift amount 0-63
|
||||
{name: "SRWconst", argLength: 1, reg: gp11, asm: "SRW", aux: "Int32"}, // unsigned arg0 >> auxint, shift amount 0-31
|
||||
|
||||
// Arithmetic shifts clobber flags.
|
||||
{name: "SRAD", argLength: 2, reg: sh21, asm: "SRAD", clobberFlags: true}, // signed arg0 >> arg1, shift amount is mod 64
|
||||
{name: "SRAW", argLength: 2, reg: sh21, asm: "SRAW", clobberFlags: true}, // signed arg0 >> arg1, shift amount is mod 32
|
||||
{name: "SRADconst", argLength: 1, reg: gp11, asm: "SRAD", aux: "Int64", clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-63
|
||||
{name: "SRAWconst", argLength: 1, reg: gp11, asm: "SRAW", aux: "Int32", clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-31
|
||||
|
||||
{name: "RLLGconst", argLength: 1, reg: gp11, asm: "RLLG", aux: "Int64"}, // arg0 rotate left auxint, rotate amount 0-63
|
||||
{name: "RLLconst", argLength: 1, reg: gp11, asm: "RLL", aux: "Int32"}, // arg0 rotate left auxint, rotate amount 0-31
|
||||
|
||||
// unary ops
|
||||
{name: "NEG", argLength: 1, reg: gp11, asm: "NEG", clobberFlags: true}, // -arg0
|
||||
{name: "NEGW", argLength: 1, reg: gp11, asm: "NEGW", clobberFlags: true}, // -arg0
|
||||
|
||||
{name: "NOT", argLength: 1, reg: gp11, resultInArg0: true, clobberFlags: true}, // ^arg0
|
||||
{name: "NOTW", argLength: 1, reg: gp11, resultInArg0: true, clobberFlags: true}, // ^arg0
|
||||
|
||||
{name: "FSQRT", argLength: 1, reg: fp11, asm: "FSQRT"}, // sqrt(arg0)
|
||||
|
||||
{name: "SUBEcarrymask", argLength: 1, reg: flagsgp, asm: "SUBE"}, // (int64)(-1) if carry is set, 0 if carry is clear.
|
||||
{name: "SUBEWcarrymask", argLength: 1, reg: flagsgp, asm: "SUBE"}, // (int32)(-1) if carry is set, 0 if carry is clear.
|
||||
// Note: 32-bits subtraction is not implemented in S390X. Temporarily use SUBE (64-bits).
|
||||
|
||||
{name: "MOVDEQ", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDEQ"}, // extract == condition from arg0
|
||||
{name: "MOVDNE", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDNE"}, // extract != condition from arg0
|
||||
{name: "MOVDLT", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDLT"}, // extract signed < condition from arg0
|
||||
{name: "MOVDLE", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDLE"}, // extract signed <= condition from arg0
|
||||
{name: "MOVDGT", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDGT"}, // extract signed > condition from arg0
|
||||
{name: "MOVDGE", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDGE"}, // extract signed >= condition from arg0
|
||||
|
||||
// Different rules for floating point conditions because
|
||||
// any comparison involving a NaN is always false and thus
|
||||
// the patterns for inverting conditions cannot be used.
|
||||
{name: "MOVDGTnoinv", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDGT"}, // extract floating > condition from arg0
|
||||
{name: "MOVDGEnoinv", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDGE"}, // extract floating >= condition from arg0
|
||||
|
||||
{name: "MOVBreg", argLength: 1, reg: gp11sp, asm: "MOVB", typ: "Int64"}, // sign extend arg0 from int8 to int64
|
||||
{name: "MOVBZreg", argLength: 1, reg: gp11sp, asm: "MOVBZ", typ: "UInt64"}, // zero extend arg0 from int8 to int64
|
||||
{name: "MOVHreg", argLength: 1, reg: gp11sp, asm: "MOVH", typ: "Int64"}, // sign extend arg0 from int16 to int64
|
||||
{name: "MOVHZreg", argLength: 1, reg: gp11sp, asm: "MOVHZ", typ: "UInt64"}, // zero extend arg0 from int16 to int64
|
||||
{name: "MOVWreg", argLength: 1, reg: gp11sp, asm: "MOVW", typ: "Int64"}, // sign extend arg0 from int32 to int64
|
||||
{name: "MOVWZreg", argLength: 1, reg: gp11sp, asm: "MOVWZ", typ: "UInt64"}, // zero extend arg0 from int32 to int64
|
||||
|
||||
{name: "MOVDconst", reg: gp01, asm: "MOVD", typ: "UInt64", aux: "Int64", rematerializeable: true}, // auxint
|
||||
|
||||
{name: "CFDBRA", argLength: 1, reg: fpgp, asm: "CFDBRA"}, // convert float64 to int32
|
||||
{name: "CGDBRA", argLength: 1, reg: fpgp, asm: "CGDBRA"}, // convert float64 to int64
|
||||
{name: "CFEBRA", argLength: 1, reg: fpgp, asm: "CFEBRA"}, // convert float32 to int32
|
||||
{name: "CGEBRA", argLength: 1, reg: fpgp, asm: "CGEBRA"}, // convert float32 to int64
|
||||
{name: "CEFBRA", argLength: 1, reg: gpfp, asm: "CEFBRA"}, // convert int32 to float32
|
||||
{name: "CDFBRA", argLength: 1, reg: gpfp, asm: "CDFBRA"}, // convert int32 to float64
|
||||
{name: "CEGBRA", argLength: 1, reg: gpfp, asm: "CEGBRA"}, // convert int64 to float32
|
||||
{name: "CDGBRA", argLength: 1, reg: gpfp, asm: "CDGBRA"}, // convert int64 to float64
|
||||
{name: "LEDBR", argLength: 1, reg: fp11, asm: "LEDBR"}, // convert float64 to float32
|
||||
{name: "LDEBR", argLength: 1, reg: fp11, asm: "LDEBR"}, // convert float32 to float64
|
||||
|
||||
{name: "MOVDaddr", argLength: 1, reg: addr, aux: "SymOff", rematerializeable: true, clobberFlags: true}, // arg0 + auxint + offset encoded in aux
|
||||
{name: "MOVDaddridx", argLength: 2, reg: addridx, aux: "SymOff", clobberFlags: true}, // arg0 + arg1 + auxint + aux
|
||||
|
||||
// auxint+aux == add auxint and the offset of the symbol in aux (if any) to the effective address
|
||||
{name: "MOVBZload", argLength: 2, reg: gpload, asm: "MOVBZ", aux: "SymOff", typ: "UInt8", clobberFlags: true}, // load byte from arg0+auxint+aux. arg1=mem. Zero extend.
|
||||
{name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVB", aux: "SymOff", clobberFlags: true}, // ditto, sign extend to int64
|
||||
{name: "MOVHZload", argLength: 2, reg: gpload, asm: "MOVHZ", aux: "SymOff", typ: "UInt16", clobberFlags: true}, // load 2 bytes from arg0+auxint+aux. arg1=mem. Zero extend.
|
||||
{name: "MOVHload", argLength: 2, reg: gpload, asm: "MOVH", aux: "SymOff", clobberFlags: true}, // ditto, sign extend to int64
|
||||
{name: "MOVWZload", argLength: 2, reg: gpload, asm: "MOVWZ", aux: "SymOff", typ: "UInt32", clobberFlags: true}, // load 4 bytes from arg0+auxint+aux. arg1=mem. Zero extend.
|
||||
{name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVW", aux: "SymOff", clobberFlags: true}, // ditto, sign extend to int64
|
||||
{name: "MOVDload", argLength: 2, reg: gpload, asm: "MOVD", aux: "SymOff", typ: "UInt64", clobberFlags: true}, // load 8 bytes from arg0+auxint+aux. arg1=mem
|
||||
|
||||
{name: "MOVHBRload", argLength: 2, reg: gpload, asm: "MOVHBR", aux: "SymOff", typ: "UInt16", clobberFlags: true}, // load 2 bytes from arg0+auxint+aux. arg1=mem. Reverse bytes.
|
||||
{name: "MOVWBRload", argLength: 2, reg: gpload, asm: "MOVWBR", aux: "SymOff", typ: "UInt32", clobberFlags: true}, // load 4 bytes from arg0+auxint+aux. arg1=mem. Reverse bytes.
|
||||
{name: "MOVDBRload", argLength: 2, reg: gpload, asm: "MOVDBR", aux: "SymOff", typ: "UInt64", clobberFlags: true}, // load 8 bytes from arg0+auxint+aux. arg1=mem. Reverse bytes.
|
||||
|
||||
{name: "MOVBstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem", clobberFlags: true}, // store byte in arg1 to arg0+auxint+aux. arg2=mem
|
||||
{name: "MOVHstore", argLength: 3, reg: gpstore, asm: "MOVH", aux: "SymOff", typ: "Mem", clobberFlags: true}, // store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem
|
||||
{name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", clobberFlags: true}, // store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem
|
||||
{name: "MOVDstore", argLength: 3, reg: gpstore, asm: "MOVD", aux: "SymOff", typ: "Mem", clobberFlags: true}, // store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem
|
||||
|
||||
{name: "MVC", argLength: 3, reg: gpmvc, asm: "MVC", aux: "SymValAndOff", typ: "Mem", clobberFlags: true}, // arg0=destptr, arg1=srcptr, arg2=mem, auxint=size,off
|
||||
|
||||
// indexed loads/stores
|
||||
// TODO(mundaym): add sign-extended indexed loads
|
||||
{name: "MOVBZloadidx", argLength: 3, reg: gploadidx, asm: "MOVBZ", aux: "SymOff", clobberFlags: true}, // load a byte from arg0+arg1+auxint+aux. arg2=mem
|
||||
{name: "MOVHZloadidx", argLength: 3, reg: gploadidx, asm: "MOVHZ", aux: "SymOff", clobberFlags: true}, // load 2 bytes from arg0+arg1+auxint+aux. arg2=mem
|
||||
{name: "MOVWZloadidx", argLength: 3, reg: gploadidx, asm: "MOVWZ", aux: "SymOff", clobberFlags: true}, // load 4 bytes from arg0+arg1+auxint+aux. arg2=mem
|
||||
{name: "MOVDloadidx", argLength: 3, reg: gploadidx, asm: "MOVD", aux: "SymOff", clobberFlags: true}, // load 8 bytes from arg0+arg1+auxint+aux. arg2=mem
|
||||
{name: "MOVHBRloadidx", argLength: 3, reg: gploadidx, asm: "MOVHBR", aux: "SymOff", clobberFlags: true}, // load 2 bytes from arg0+arg1+auxint+aux. arg2=mem. Reverse bytes.
|
||||
{name: "MOVWBRloadidx", argLength: 3, reg: gploadidx, asm: "MOVWBR", aux: "SymOff", clobberFlags: true}, // load 4 bytes from arg0+arg1+auxint+aux. arg2=mem. Reverse bytes.
|
||||
{name: "MOVDBRloadidx", argLength: 3, reg: gploadidx, asm: "MOVDBR", aux: "SymOff", clobberFlags: true}, // load 8 bytes from arg0+arg1+auxint+aux. arg2=mem. Reverse bytes.
|
||||
{name: "MOVBstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVB", aux: "SymOff", clobberFlags: true}, // store byte in arg2 to arg0+arg1+auxint+aux. arg3=mem
|
||||
{name: "MOVHstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVH", aux: "SymOff", clobberFlags: true}, // store 2 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
|
||||
{name: "MOVWstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVW", aux: "SymOff", clobberFlags: true}, // store 4 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
|
||||
{name: "MOVDstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVD", aux: "SymOff", clobberFlags: true}, // store 8 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
|
||||
|
||||
// For storeconst ops, the AuxInt field encodes both
|
||||
// the value to store and an address offset of the store.
|
||||
// Cast AuxInt to a ValAndOff to extract Val and Off fields.
|
||||
{name: "MOVBstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVB", aux: "SymValAndOff", typ: "Mem", clobberFlags: true}, // store low byte of ValAndOff(AuxInt).Val() to arg0+ValAndOff(AuxInt).Off()+aux. arg1=mem
|
||||
{name: "MOVHstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVH", aux: "SymValAndOff", typ: "Mem", clobberFlags: true}, // store low 2 bytes of ...
|
||||
{name: "MOVWstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVW", aux: "SymValAndOff", typ: "Mem", clobberFlags: true}, // store low 4 bytes of ...
|
||||
{name: "MOVDstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVD", aux: "SymValAndOff", typ: "Mem", clobberFlags: true}, // store 8 bytes of ...
|
||||
|
||||
{name: "CLEAR", argLength: 2, reg: regInfo{inputs: []regMask{ptr, 0}}, asm: "CLEAR", aux: "SymValAndOff", typ: "Mem", clobberFlags: true},
|
||||
|
||||
{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true}, // call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem
|
||||
{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{ptrsp, buildReg("R12"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
|
||||
{name: "CALLdefer", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call deferproc. arg0=mem, auxint=argsize, returns mem
|
||||
{name: "CALLgo", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call newproc. arg0=mem, auxint=argsize, returns mem
|
||||
{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{ptr}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
|
||||
|
||||
// (InvertFlags (CMP a b)) == (CMP b a)
|
||||
// InvertFlags is a pseudo-op which can't appear in assembly output.
|
||||
{name: "InvertFlags", argLength: 1}, // reverse direction of arg0
|
||||
|
||||
// Pseudo-ops
|
||||
{name: "LoweredGetG", argLength: 1, reg: gp01}, // arg0=mem
|
||||
// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
|
||||
// and sorts it to the very beginning of the block to prevent other
|
||||
// use of R12 (the closure pointer)
|
||||
{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R12")}}},
|
||||
// arg0=ptr,arg1=mem, returns void. Faults if ptr is nil.
|
||||
{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{ptrsp}}, clobberFlags: true},
|
||||
|
||||
// MOVDconvert converts between pointers and integers.
|
||||
// We have a special op for this so as to not confuse GC
|
||||
// (particularly stack maps). It takes a memory arg so it
|
||||
// gets correctly ordered with respect to GC safepoints.
|
||||
// arg0=ptr/int arg1=mem, output=int/ptr
|
||||
{name: "MOVDconvert", argLength: 2, reg: gp11sp, asm: "MOVD"},
|
||||
|
||||
// Constant flag values. For any comparison, there are 5 possible
|
||||
// outcomes: the three from the signed total order (<,==,>) and the
|
||||
// three from the unsigned total order. The == cases overlap.
|
||||
// Note: there's a sixth "unordered" outcome for floating-point
|
||||
// comparisons, but we don't use such a beast yet.
|
||||
// These ops are for temporary use by rewrite rules. They
|
||||
// cannot appear in the generated assembly.
|
||||
{name: "FlagEQ"}, // equal
|
||||
{name: "FlagLT"}, // <
|
||||
{name: "FlagGT"}, // >
|
||||
|
||||
// store multiple
|
||||
{
|
||||
name: "STMG2",
|
||||
argLength: 4,
|
||||
reg: regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), 0}},
|
||||
aux: "SymOff",
|
||||
typ: "Mem",
|
||||
asm: "STMG",
|
||||
},
|
||||
{
|
||||
name: "STMG3",
|
||||
argLength: 5,
|
||||
reg: regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), buildReg("R3"), 0}},
|
||||
aux: "SymOff",
|
||||
typ: "Mem",
|
||||
asm: "STMG",
|
||||
},
|
||||
{
|
||||
name: "STMG4",
|
||||
argLength: 6,
|
||||
reg: regInfo{inputs: []regMask{
|
||||
ptrsp,
|
||||
buildReg("R1"),
|
||||
buildReg("R2"),
|
||||
buildReg("R3"),
|
||||
buildReg("R4"),
|
||||
0,
|
||||
}},
|
||||
aux: "SymOff",
|
||||
typ: "Mem",
|
||||
asm: "STMG",
|
||||
},
|
||||
{
|
||||
name: "STM2",
|
||||
argLength: 4,
|
||||
reg: regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), 0}},
|
||||
aux: "SymOff",
|
||||
typ: "Mem",
|
||||
asm: "STMY",
|
||||
},
|
||||
{
|
||||
name: "STM3",
|
||||
argLength: 5,
|
||||
reg: regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), buildReg("R3"), 0}},
|
||||
aux: "SymOff",
|
||||
typ: "Mem",
|
||||
asm: "STMY",
|
||||
},
|
||||
{
|
||||
name: "STM4",
|
||||
argLength: 6,
|
||||
reg: regInfo{inputs: []regMask{
|
||||
ptrsp,
|
||||
buildReg("R1"),
|
||||
buildReg("R2"),
|
||||
buildReg("R3"),
|
||||
buildReg("R4"),
|
||||
0,
|
||||
}},
|
||||
aux: "SymOff",
|
||||
typ: "Mem",
|
||||
asm: "STMY",
|
||||
},
|
||||
|
||||
// large move
|
||||
// auxint = remaining bytes after loop (rem)
|
||||
// arg0 = address of dst memory (in R1, changed as a side effect)
|
||||
// arg1 = address of src memory (in R2, changed as a side effect)
|
||||
// arg2 = pointer to last address to move in loop + 256
|
||||
// arg3 = mem
|
||||
// returns mem
|
||||
//
|
||||
// mvc: MVC $256, 0(R2), 0(R1)
|
||||
// MOVD $256(R1), R1
|
||||
// MOVD $256(R2), R2
|
||||
// CMP R2, Rarg2
|
||||
// BNE mvc
|
||||
// MVC $rem, 0(R2), 0(R1) // if rem > 0
|
||||
{
|
||||
name: "LoweredMove",
|
||||
aux: "Int64",
|
||||
argLength: 4,
|
||||
reg: regInfo{
|
||||
inputs: []regMask{buildReg("R1"), buildReg("R2"), gpsp},
|
||||
clobbers: buildReg("R1 R2"),
|
||||
},
|
||||
clobberFlags: true,
|
||||
typ: "Mem",
|
||||
},
|
||||
|
||||
// large clear
|
||||
// auxint = remaining bytes after loop (rem)
|
||||
// arg0 = address of dst memory (in R1, changed as a side effect)
|
||||
// arg1 = pointer to last address to zero in loop + 256
|
||||
// arg2 = mem
|
||||
// returns mem
|
||||
//
|
||||
// clear: CLEAR $256, 0(R1)
|
||||
// MOVD $256(R1), R1
|
||||
// CMP R1, Rarg2
|
||||
// BNE clear
|
||||
// CLEAR $rem, 0(R1) // if rem > 0
|
||||
{
|
||||
name: "LoweredZero",
|
||||
aux: "Int64",
|
||||
argLength: 3,
|
||||
reg: regInfo{
|
||||
inputs: []regMask{buildReg("R1"), gpsp},
|
||||
clobbers: buildReg("R1"),
|
||||
},
|
||||
clobberFlags: true,
|
||||
typ: "Mem",
|
||||
},
|
||||
}
|
||||
|
||||
var S390Xblocks = []blockData{
|
||||
{name: "EQ"},
|
||||
{name: "NE"},
|
||||
{name: "LT"},
|
||||
{name: "LE"},
|
||||
{name: "GT"},
|
||||
{name: "GE"},
|
||||
{name: "GTF"}, // FP comparison
|
||||
{name: "GEF"}, // FP comparison
|
||||
}
|
||||
|
||||
archs = append(archs, arch{
|
||||
name: "S390X",
|
||||
pkg: "cmd/internal/obj/s390x",
|
||||
genfile: "../../s390x/ssa.go",
|
||||
ops: S390Xops,
|
||||
blocks: S390Xblocks,
|
||||
regnames: regNamesS390X,
|
||||
gpregmask: gp,
|
||||
fpregmask: fp,
|
||||
framepointerreg: -1, // not used
|
||||
})
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -502,6 +502,8 @@ func (s *regAllocState) init(f *Func) {
|
||||
// we do need to be careful, but that carefulness is hidden
|
||||
// in the rewrite rules so we always have a free register
|
||||
// available for global load/stores. See gen/386.rules (search for Flag_shared).
|
||||
case "s390x":
|
||||
// nothing to do, R10 & R11 already reserved
|
||||
default:
|
||||
s.f.Config.fe.Unimplementedf(0, "arch %s not implemented", s.f.Config.arch)
|
||||
}
|
||||
|
@ -228,6 +228,11 @@ func is16Bit(n int64) bool {
|
||||
return n == int64(int16(n))
|
||||
}
|
||||
|
||||
// is20Bit reports whether n can be represented as a signed 20 bit integer.
|
||||
func is20Bit(n int64) bool {
|
||||
return -(1<<19) <= n && n < (1<<19)
|
||||
}
|
||||
|
||||
// b2i translates a boolean value to 0 or 1 for assigning to auxInt.
|
||||
func b2i(b bool) int64 {
|
||||
if b {
|
||||
|
15822
src/cmd/compile/internal/ssa/rewriteS390X.go
Normal file
15822
src/cmd/compile/internal/ssa/rewriteS390X.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -84,7 +84,10 @@ func schedule(f *Func) {
|
||||
// Compute score. Larger numbers are scheduled closer to the end of the block.
|
||||
for _, v := range b.Values {
|
||||
switch {
|
||||
case v.Op == OpAMD64LoweredGetClosurePtr || v.Op == OpPPC64LoweredGetClosurePtr || v.Op == OpARMLoweredGetClosurePtr || v.Op == OpARM64LoweredGetClosurePtr || v.Op == Op386LoweredGetClosurePtr || v.Op == OpMIPS64LoweredGetClosurePtr:
|
||||
case v.Op == OpAMD64LoweredGetClosurePtr || v.Op == OpPPC64LoweredGetClosurePtr ||
|
||||
v.Op == OpARMLoweredGetClosurePtr || v.Op == OpARM64LoweredGetClosurePtr ||
|
||||
v.Op == Op386LoweredGetClosurePtr || v.Op == OpMIPS64LoweredGetClosurePtr ||
|
||||
v.Op == OpS390XLoweredGetClosurePtr:
|
||||
// We also score GetLoweredClosurePtr as early as possible to ensure that the
|
||||
// context register is not stomped. GetLoweredClosurePtr should only appear
|
||||
// in the entry block where there are no phi functions, so there is no
|
||||
|
@ -1,4 +1,4 @@
|
||||
// +build !amd64,!arm,!amd64p32,!386,!arm64,!ppc64le,!mips64,!mips64le
|
||||
// +build !amd64,!arm,!amd64p32,!386,!arm64,!ppc64le,!mips64,!mips64le,!s390x
|
||||
// errorcheck -0 -l -live -wb=0
|
||||
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// +build amd64 arm amd64p32 386 arm64 mips64 mips64le
|
||||
// +build amd64 arm amd64p32 386 arm64 mips64 mips64le s390x
|
||||
// errorcheck -0 -l -live -wb=0
|
||||
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
|
@ -1,5 +1,5 @@
|
||||
// errorcheck -0 -d=nil
|
||||
// +build amd64 arm amd64p32 386 arm64 mips64 mips64le ppc64le
|
||||
// +build amd64 arm amd64p32 386 arm64 mips64 mips64le ppc64le s390x
|
||||
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
|
@ -1,4 +1,4 @@
|
||||
// +build amd64
|
||||
// +build amd64,s390x
|
||||
// errorcheck -0 -d=ssa/phiopt/debug=3
|
||||
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// +build !amd64,!arm,!amd64p32,!386,!arm64,!ppc64le,!mips64,!mips64le
|
||||
// +build !amd64,!arm,!amd64p32,!386,!arm64,!ppc64le,!mips64,!mips64le,!s390x
|
||||
// errorcheck -0 -d=append,slice
|
||||
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
|
Loading…
Reference in New Issue
Block a user