blob: 193611e77e884db0671c6238a8d31f346b997953 [file]
// 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 mips
import (
"math"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/logopt"
"cmd/compile/internal/ssa"
"cmd/compile/internal/ssa/block"
"cmd/compile/internal/ssa/ssaop"
"cmd/compile/internal/ssagen"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/mips"
"internal/abi"
)
// isFPreg reports whether r is an FP register.
func isFPreg(r int16) bool {
return mips.REG_F0 <= r && r <= mips.REG_F31
}
// loadByType returns the load instruction of the given type.
func loadByType(t *types.Type, r int16) obj.As {
if isFPreg(r) {
if t.Size() == 4 { // float32 or int32
return mips.AMOVF
} else { // float64 or int64
return mips.AMOVD
}
} else {
switch t.Size() {
case 1:
if t.IsSigned() {
return mips.AMOVB
} else {
return mips.AMOVBU
}
case 2:
if t.IsSigned() {
return mips.AMOVH
} else {
return mips.AMOVHU
}
case 4:
return mips.AMOVW
}
}
panic("bad load type")
}
// storeByType returns the store instruction of the given type.
func storeByType(t *types.Type, r int16) obj.As {
if isFPreg(r) {
if t.Size() == 4 { // float32 or int32
return mips.AMOVF
} else { // float64 or int64
return mips.AMOVD
}
} else {
switch t.Size() {
case 1:
return mips.AMOVB
case 2:
return mips.AMOVH
case 4:
return mips.AMOVW
}
}
panic("bad store type")
}
func ssaGenValue(s *ssagen.State, v *ssa.Value) {
switch v.Op {
case ssaop.OpCopy, ssaop.OpMIPSMOVWreg:
t := v.Type
if t.IsMemory() {
return
}
x := v.Args[0].Reg()
y := v.Reg()
if x == y {
return
}
as := mips.AMOVW
if isFPreg(x) && isFPreg(y) {
as = mips.AMOVF
if t.Size() == 8 {
as = mips.AMOVD
}
}
p := s.Prog(as)
p.From.Type = obj.TYPE_REG
p.From.Reg = x
p.To.Type = obj.TYPE_REG
p.To.Reg = y
case ssaop.OpMIPSMOVWnop:
// nothing to do
case ssaop.OpLoadReg:
if v.Type.IsFlags() {
v.Fatalf("load flags not implemented: %v", v.LongString())
return
}
r := v.Reg()
p := s.Prog(loadByType(v.Type, r))
ssagen.AddrAuto(&p.From, v.Args[0])
p.To.Type = obj.TYPE_REG
p.To.Reg = r
case ssaop.OpStoreReg:
if v.Type.IsFlags() {
v.Fatalf("store flags not implemented: %v", v.LongString())
return
}
r := v.Args[0].Reg()
p := s.Prog(storeByType(v.Type, r))
p.From.Type = obj.TYPE_REG
p.From.Reg = r
ssagen.AddrAuto(&p.To, v)
case ssaop.OpMIPSADD,
ssaop.OpMIPSSUB,
ssaop.OpMIPSAND,
ssaop.OpMIPSOR,
ssaop.OpMIPSXOR,
ssaop.OpMIPSNOR,
ssaop.OpMIPSSLL,
ssaop.OpMIPSSRL,
ssaop.OpMIPSSRA,
ssaop.OpMIPSADDF,
ssaop.OpMIPSADDD,
ssaop.OpMIPSSUBF,
ssaop.OpMIPSSUBD,
ssaop.OpMIPSMULF,
ssaop.OpMIPSMULD,
ssaop.OpMIPSDIVF,
ssaop.OpMIPSDIVD,
ssaop.OpMIPSMUL:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[1].Reg()
p.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssaop.OpMIPSSGT,
ssaop.OpMIPSSGTU:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
p.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssaop.OpMIPSSGTzero,
ssaop.OpMIPSSGTUzero:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
p.Reg = mips.REGZERO
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssaop.OpMIPSADDconst,
ssaop.OpMIPSSUBconst,
ssaop.OpMIPSANDconst,
ssaop.OpMIPSORconst,
ssaop.OpMIPSXORconst,
ssaop.OpMIPSSLLconst,
ssaop.OpMIPSSRLconst,
ssaop.OpMIPSSRAconst,
ssaop.OpMIPSSGTconst,
ssaop.OpMIPSSGTUconst:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_CONST
p.From.Offset = v.AuxInt
p.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssaop.OpMIPSMULT,
ssaop.OpMIPSMULTU,
ssaop.OpMIPSDIV,
ssaop.OpMIPSDIVU:
// HI, LO results exist in low quality registers that can't
// be stored without using REGTMP.
// This used to cause corruptions due to nested REGTMP issues
// since store might also need REGTMP to materialize the address.
// Instead we move the result into high quality registers.
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[1].Reg()
p.Reg = v.Args[0].Reg()
p1 := s.Prog(mips.AMOVW)
p1.From.Type = obj.TYPE_REG
p1.From.Reg = mips.REG_HI
p1.To.Type = obj.TYPE_REG
p1.To.Reg = v.Reg0()
p2 := s.Prog(mips.AMOVW)
p2.From.Type = obj.TYPE_REG
p2.From.Reg = mips.REG_LO
p2.To.Type = obj.TYPE_REG
p2.To.Reg = v.Reg1()
case ssaop.OpMIPSMOVWconst:
r := v.Reg()
p := s.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
if isFPreg(r) {
// cannot move into FP registers, use TMP as intermediate
p.To.Reg = mips.REGTMP
p = s.Prog(mips.AMOVW)
p.From.Type = obj.TYPE_REG
p.From.Reg = mips.REGTMP
p.To.Type = obj.TYPE_REG
p.To.Reg = r
}
case ssaop.OpMIPSMOVFconst,
ssaop.OpMIPSMOVDconst:
p := s.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 = v.Reg()
case ssaop.OpMIPSCMOVZ:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[2].Reg()
p.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssaop.OpMIPSCMOVZzero:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[1].Reg()
p.Reg = mips.REGZERO
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssaop.OpMIPSCMPEQF,
ssaop.OpMIPSCMPEQD,
ssaop.OpMIPSCMPGEF,
ssaop.OpMIPSCMPGED,
ssaop.OpMIPSCMPGTF,
ssaop.OpMIPSCMPGTD:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
p.Reg = v.Args[1].Reg()
case ssaop.OpMIPSMOVWaddr:
p := s.Prog(mips.AMOVW)
p.From.Type = obj.TYPE_ADDR
p.From.Reg = v.Args[0].Reg()
var wantreg string
// MOVW $sym+off(base), R
// the assembler expands it as the following:
// - base is SP: add constant offset to SP (R29)
// when constant is large, tmp register (R23) may be used
// - base is SB: load external address with relocation
switch v.Aux.(type) {
default:
v.Fatalf("aux is of unknown type %T", v.Aux)
case *obj.LSym:
wantreg = "SB"
ssagen.AddAux(&p.From, v)
case *ir.Name:
wantreg = "SP"
ssagen.AddAux(&p.From, v)
case nil:
// No sym, just MOVW $off(SP), R
wantreg = "SP"
p.From.Offset = v.AuxInt
}
if reg := v.Args[0].RegName(); reg != wantreg {
v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg)
}
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssaop.OpMIPSMOVBload,
ssaop.OpMIPSMOVBUload,
ssaop.OpMIPSMOVHload,
ssaop.OpMIPSMOVHUload,
ssaop.OpMIPSMOVWload,
ssaop.OpMIPSMOVFload,
ssaop.OpMIPSMOVDload:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssaop.OpMIPSMOVBstore,
ssaop.OpMIPSMOVHstore,
ssaop.OpMIPSMOVWstore,
ssaop.OpMIPSMOVFstore,
ssaop.OpMIPSMOVDstore:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
ssagen.AddAux(&p.To, v)
case ssaop.OpMIPSMOVBstorezero,
ssaop.OpMIPSMOVHstorezero,
ssaop.OpMIPSMOVWstorezero:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = mips.REGZERO
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
ssagen.AddAux(&p.To, v)
case ssaop.OpMIPSMOVBreg,
ssaop.OpMIPSMOVBUreg,
ssaop.OpMIPSMOVHreg,
ssaop.OpMIPSMOVHUreg:
a := v.Args[0]
for a.Op == ssaop.OpCopy || a.Op == ssaop.OpMIPSMOVWreg || a.Op == ssaop.OpMIPSMOVWnop {
a = a.Args[0]
}
if a.Op == ssaop.OpLoadReg {
t := a.Type
switch {
case v.Op == ssaop.OpMIPSMOVBreg && t.Size() == 1 && t.IsSigned(),
v.Op == ssaop.OpMIPSMOVBUreg && t.Size() == 1 && !t.IsSigned(),
v.Op == ssaop.OpMIPSMOVHreg && t.Size() == 2 && t.IsSigned(),
v.Op == ssaop.OpMIPSMOVHUreg && t.Size() == 2 && !t.IsSigned():
// arg is a proper-typed load, already zero/sign-extended, don't extend again
if v.Reg() == v.Args[0].Reg() {
return
}
p := s.Prog(mips.AMOVW)
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
return
default:
}
}
fallthrough
case ssaop.OpMIPSMOVWF,
ssaop.OpMIPSMOVWD,
ssaop.OpMIPSTRUNCFW,
ssaop.OpMIPSTRUNCDW,
ssaop.OpMIPSMOVFD,
ssaop.OpMIPSMOVDF,
ssaop.OpMIPSMOVWfpgp,
ssaop.OpMIPSMOVWgpfp,
ssaop.OpMIPSNEGF,
ssaop.OpMIPSNEGD,
ssaop.OpMIPSABSD,
ssaop.OpMIPSSQRTF,
ssaop.OpMIPSSQRTD,
ssaop.OpMIPSCLZ:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssaop.OpMIPSNEG:
// SUB from REGZERO
p := s.Prog(mips.ASUBU)
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
p.Reg = mips.REGZERO
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssaop.OpMIPSLoweredZero:
// SUBU $4, R1
// MOVW R0, 4(R1)
// ADDU $4, R1
// BNE Rarg1, R1, -2(PC)
// arg1 is the address of the last element to zero
var sz int64
var mov obj.As
switch {
case v.AuxInt%4 == 0:
sz = 4
mov = mips.AMOVW
case v.AuxInt%2 == 0:
sz = 2
mov = mips.AMOVH
default:
sz = 1
mov = mips.AMOVB
}
p := s.Prog(mips.ASUBU)
p.From.Type = obj.TYPE_CONST
p.From.Offset = sz
p.To.Type = obj.TYPE_REG
p.To.Reg = mips.REG_R1
p2 := s.Prog(mov)
p2.From.Type = obj.TYPE_REG
p2.From.Reg = mips.REGZERO
p2.To.Type = obj.TYPE_MEM
p2.To.Reg = mips.REG_R1
p2.To.Offset = sz
p3 := s.Prog(mips.AADDU)
p3.From.Type = obj.TYPE_CONST
p3.From.Offset = sz
p3.To.Type = obj.TYPE_REG
p3.To.Reg = mips.REG_R1
p4 := s.Prog(mips.ABNE)
p4.From.Type = obj.TYPE_REG
p4.From.Reg = v.Args[1].Reg()
p4.Reg = mips.REG_R1
p4.To.Type = obj.TYPE_BRANCH
p4.To.SetTarget(p2)
case ssaop.OpMIPSLoweredMove:
// SUBU $4, R1
// MOVW 4(R1), Rtmp
// MOVW Rtmp, (R2)
// ADDU $4, R1
// ADDU $4, R2
// BNE Rarg2, R1, -4(PC)
// arg2 is the address of the last element of src
var sz int64
var mov obj.As
switch {
case v.AuxInt%4 == 0:
sz = 4
mov = mips.AMOVW
case v.AuxInt%2 == 0:
sz = 2
mov = mips.AMOVH
default:
sz = 1
mov = mips.AMOVB
}
p := s.Prog(mips.ASUBU)
p.From.Type = obj.TYPE_CONST
p.From.Offset = sz
p.To.Type = obj.TYPE_REG
p.To.Reg = mips.REG_R1
p2 := s.Prog(mov)
p2.From.Type = obj.TYPE_MEM
p2.From.Reg = mips.REG_R1
p2.From.Offset = sz
p2.To.Type = obj.TYPE_REG
p2.To.Reg = mips.REGTMP
p3 := s.Prog(mov)
p3.From.Type = obj.TYPE_REG
p3.From.Reg = mips.REGTMP
p3.To.Type = obj.TYPE_MEM
p3.To.Reg = mips.REG_R2
p4 := s.Prog(mips.AADDU)
p4.From.Type = obj.TYPE_CONST
p4.From.Offset = sz
p4.To.Type = obj.TYPE_REG
p4.To.Reg = mips.REG_R1
p5 := s.Prog(mips.AADDU)
p5.From.Type = obj.TYPE_CONST
p5.From.Offset = sz
p5.To.Type = obj.TYPE_REG
p5.To.Reg = mips.REG_R2
p6 := s.Prog(mips.ABNE)
p6.From.Type = obj.TYPE_REG
p6.From.Reg = v.Args[2].Reg()
p6.Reg = mips.REG_R1
p6.To.Type = obj.TYPE_BRANCH
p6.To.SetTarget(p2)
case ssaop.OpMIPSCALLstatic, ssaop.OpMIPSCALLclosure, ssaop.OpMIPSCALLinter:
s.Call(v)
case ssaop.OpMIPSCALLtail, ssaop.OpMIPSCALLtailinter:
s.TailCall(v)
case ssaop.OpMIPSLoweredWB:
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
// AuxInt encodes how many buffer entries we need.
p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1]
case ssaop.OpMIPSLoweredPanicBoundsRR, ssaop.OpMIPSLoweredPanicBoundsRC, ssaop.OpMIPSLoweredPanicBoundsCR, ssaop.OpMIPSLoweredPanicBoundsCC,
ssaop.OpMIPSLoweredPanicExtendRR, ssaop.OpMIPSLoweredPanicExtendRC:
// Compute the constant we put in the PCData entry for this call.
code, signed := ssa.BoundsKind(v.AuxInt).Code()
xIsReg := false
yIsReg := false
xVal := 0
yVal := 0
extend := false
switch v.Op {
case ssaop.OpMIPSLoweredPanicBoundsRR:
xIsReg = true
xVal = int(v.Args[0].Reg() - mips.REG_R1)
yIsReg = true
yVal = int(v.Args[1].Reg() - mips.REG_R1)
case ssaop.OpMIPSLoweredPanicExtendRR:
extend = true
xIsReg = true
hi := int(v.Args[0].Reg() - mips.REG_R1)
lo := int(v.Args[1].Reg() - mips.REG_R1)
xVal = hi<<2 + lo // encode 2 register numbers
yIsReg = true
yVal = int(v.Args[2].Reg() - mips.REG_R1)
case ssaop.OpMIPSLoweredPanicBoundsRC:
xIsReg = true
xVal = int(v.Args[0].Reg() - mips.REG_R1)
c := v.Aux.(ssa.PanicBoundsC).C
if c >= 0 && c <= abi.BoundsMaxConst {
yVal = int(c)
} else {
// Move constant to a register
yIsReg = true
if yVal == xVal {
yVal = 1
}
p := s.Prog(mips.AMOVW)
p.From.Type = obj.TYPE_CONST
p.From.Offset = c
p.To.Type = obj.TYPE_REG
p.To.Reg = mips.REG_R1 + int16(yVal)
}
case ssaop.OpMIPSLoweredPanicExtendRC:
extend = true
xIsReg = true
hi := int(v.Args[0].Reg() - mips.REG_R1)
lo := int(v.Args[1].Reg() - mips.REG_R1)
xVal = hi<<2 + lo // encode 2 register numbers
c := v.Aux.(ssa.PanicBoundsC).C
if c >= 0 && c <= abi.BoundsMaxConst {
yVal = int(c)
} else {
// Move constant to a register
for yVal == hi || yVal == lo {
yVal++
}
p := s.Prog(mips.AMOVW)
p.From.Type = obj.TYPE_CONST
p.From.Offset = c
p.To.Type = obj.TYPE_REG
p.To.Reg = mips.REG_R1 + int16(yVal)
}
case ssaop.OpMIPSLoweredPanicBoundsCR:
yIsReg = true
yVal = int(v.Args[0].Reg() - mips.REG_R1)
c := v.Aux.(ssa.PanicBoundsC).C
if c >= 0 && c <= abi.BoundsMaxConst {
xVal = int(c)
} else if signed && int64(int32(c)) == c || !signed && int64(uint32(c)) == c {
// Move constant to a register
xIsReg = true
if xVal == yVal {
xVal = 1
}
p := s.Prog(mips.AMOVW)
p.From.Type = obj.TYPE_CONST
p.From.Offset = c
p.To.Type = obj.TYPE_REG
p.To.Reg = mips.REG_R1 + int16(xVal)
} else {
// Move constant to two registers
extend = true
xIsReg = true
hi := 0
lo := 1
if hi == yVal {
hi = 2
}
if lo == yVal {
lo = 2
}
xVal = hi<<2 + lo
p := s.Prog(mips.AMOVW)
p.From.Type = obj.TYPE_CONST
p.From.Offset = c >> 32
p.To.Type = obj.TYPE_REG
p.To.Reg = mips.REG_R1 + int16(hi)
p = s.Prog(mips.AMOVW)
p.From.Type = obj.TYPE_CONST
p.From.Offset = int64(int32(c))
p.To.Type = obj.TYPE_REG
p.To.Reg = mips.REG_R1 + int16(lo)
}
case ssaop.OpMIPSLoweredPanicBoundsCC:
c := v.Aux.(ssa.PanicBoundsCC).Cx
if c >= 0 && c <= abi.BoundsMaxConst {
xVal = int(c)
} else if signed && int64(int32(c)) == c || !signed && int64(uint32(c)) == c {
// Move constant to a register
xIsReg = true
p := s.Prog(mips.AMOVW)
p.From.Type = obj.TYPE_CONST
p.From.Offset = c
p.To.Type = obj.TYPE_REG
p.To.Reg = mips.REG_R1 + int16(xVal)
} else {
// Move constant to two registers
extend = true
xIsReg = true
hi := 0
lo := 1
xVal = hi<<2 + lo
p := s.Prog(mips.AMOVW)
p.From.Type = obj.TYPE_CONST
p.From.Offset = c >> 32
p.To.Type = obj.TYPE_REG
p.To.Reg = mips.REG_R1 + int16(hi)
p = s.Prog(mips.AMOVW)
p.From.Type = obj.TYPE_CONST
p.From.Offset = int64(int32(c))
p.To.Type = obj.TYPE_REG
p.To.Reg = mips.REG_R1 + int16(lo)
}
c = v.Aux.(ssa.PanicBoundsCC).Cy
if c >= 0 && c <= abi.BoundsMaxConst {
yVal = int(c)
} else {
// Move constant to a register
yIsReg = true
yVal = 2
p := s.Prog(mips.AMOVW)
p.From.Type = obj.TYPE_CONST
p.From.Offset = c
p.To.Type = obj.TYPE_REG
p.To.Reg = mips.REG_R1 + int16(yVal)
}
}
c := abi.BoundsEncode(code, signed, xIsReg, yIsReg, xVal, yVal)
p := s.Prog(obj.APCDATA)
p.From.SetConst(abi.PCDATA_PanicBounds)
p.To.SetConst(int64(c))
p = s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
if extend {
p.To.Sym = ir.Syms.PanicExtend
} else {
p.To.Sym = ir.Syms.PanicBounds
}
case ssaop.OpMIPSLoweredAtomicLoad8,
ssaop.OpMIPSLoweredAtomicLoad32:
s.Prog(mips.ASYNC)
var op obj.As
switch v.Op {
case ssaop.OpMIPSLoweredAtomicLoad8:
op = mips.AMOVB
case ssaop.OpMIPSLoweredAtomicLoad32:
op = mips.AMOVW
}
p := s.Prog(op)
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg0()
s.Prog(mips.ASYNC)
case ssaop.OpMIPSLoweredAtomicStore8,
ssaop.OpMIPSLoweredAtomicStore32:
s.Prog(mips.ASYNC)
var op obj.As
switch v.Op {
case ssaop.OpMIPSLoweredAtomicStore8:
op = mips.AMOVB
case ssaop.OpMIPSLoweredAtomicStore32:
op = mips.AMOVW
}
p := s.Prog(op)
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
s.Prog(mips.ASYNC)
case ssaop.OpMIPSLoweredAtomicStorezero:
s.Prog(mips.ASYNC)
p := s.Prog(mips.AMOVW)
p.From.Type = obj.TYPE_REG
p.From.Reg = mips.REGZERO
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
s.Prog(mips.ASYNC)
case ssaop.OpMIPSLoweredAtomicExchange:
// SYNC
// MOVW Rarg1, Rtmp
// LL (Rarg0), Rout
// SC Rtmp, (Rarg0)
// BEQ Rtmp, -3(PC)
// SYNC
s.Prog(mips.ASYNC)
p := s.Prog(mips.AMOVW)
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = mips.REGTMP
p1 := s.Prog(mips.ALL)
p1.From.Type = obj.TYPE_MEM
p1.From.Reg = v.Args[0].Reg()
p1.To.Type = obj.TYPE_REG
p1.To.Reg = v.Reg0()
p2 := s.Prog(mips.ASC)
p2.From.Type = obj.TYPE_REG
p2.From.Reg = mips.REGTMP
p2.To.Type = obj.TYPE_MEM
p2.To.Reg = v.Args[0].Reg()
p3 := s.Prog(mips.ABEQ)
p3.From.Type = obj.TYPE_REG
p3.From.Reg = mips.REGTMP
p3.To.Type = obj.TYPE_BRANCH
p3.To.SetTarget(p)
s.Prog(mips.ASYNC)
case ssaop.OpMIPSLoweredAtomicAdd:
// SYNC
// LL (Rarg0), Rout
// ADDU Rarg1, Rout, Rtmp
// SC Rtmp, (Rarg0)
// BEQ Rtmp, -3(PC)
// SYNC
// ADDU Rarg1, Rout
s.Prog(mips.ASYNC)
p := s.Prog(mips.ALL)
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg0()
p1 := s.Prog(mips.AADDU)
p1.From.Type = obj.TYPE_REG
p1.From.Reg = v.Args[1].Reg()
p1.Reg = v.Reg0()
p1.To.Type = obj.TYPE_REG
p1.To.Reg = mips.REGTMP
p2 := s.Prog(mips.ASC)
p2.From.Type = obj.TYPE_REG
p2.From.Reg = mips.REGTMP
p2.To.Type = obj.TYPE_MEM
p2.To.Reg = v.Args[0].Reg()
p3 := s.Prog(mips.ABEQ)
p3.From.Type = obj.TYPE_REG
p3.From.Reg = mips.REGTMP
p3.To.Type = obj.TYPE_BRANCH
p3.To.SetTarget(p)
s.Prog(mips.ASYNC)
p4 := s.Prog(mips.AADDU)
p4.From.Type = obj.TYPE_REG
p4.From.Reg = v.Args[1].Reg()
p4.Reg = v.Reg0()
p4.To.Type = obj.TYPE_REG
p4.To.Reg = v.Reg0()
case ssaop.OpMIPSLoweredAtomicAddconst:
// SYNC
// LL (Rarg0), Rout
// ADDU $auxInt, Rout, Rtmp
// SC Rtmp, (Rarg0)
// BEQ Rtmp, -3(PC)
// SYNC
// ADDU $auxInt, Rout
s.Prog(mips.ASYNC)
p := s.Prog(mips.ALL)
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg0()
p1 := s.Prog(mips.AADDU)
p1.From.Type = obj.TYPE_CONST
p1.From.Offset = v.AuxInt
p1.Reg = v.Reg0()
p1.To.Type = obj.TYPE_REG
p1.To.Reg = mips.REGTMP
p2 := s.Prog(mips.ASC)
p2.From.Type = obj.TYPE_REG
p2.From.Reg = mips.REGTMP
p2.To.Type = obj.TYPE_MEM
p2.To.Reg = v.Args[0].Reg()
p3 := s.Prog(mips.ABEQ)
p3.From.Type = obj.TYPE_REG
p3.From.Reg = mips.REGTMP
p3.To.Type = obj.TYPE_BRANCH
p3.To.SetTarget(p)
s.Prog(mips.ASYNC)
p4 := s.Prog(mips.AADDU)
p4.From.Type = obj.TYPE_CONST
p4.From.Offset = v.AuxInt
p4.Reg = v.Reg0()
p4.To.Type = obj.TYPE_REG
p4.To.Reg = v.Reg0()
case ssaop.OpMIPSLoweredAtomicAnd,
ssaop.OpMIPSLoweredAtomicOr:
// SYNC
// LL (Rarg0), Rtmp
// AND/OR Rarg1, Rtmp
// SC Rtmp, (Rarg0)
// BEQ Rtmp, -3(PC)
// SYNC
s.Prog(mips.ASYNC)
p := s.Prog(mips.ALL)
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = mips.REGTMP
p1 := s.Prog(v.Op.Asm())
p1.From.Type = obj.TYPE_REG
p1.From.Reg = v.Args[1].Reg()
p1.Reg = mips.REGTMP
p1.To.Type = obj.TYPE_REG
p1.To.Reg = mips.REGTMP
p2 := s.Prog(mips.ASC)
p2.From.Type = obj.TYPE_REG
p2.From.Reg = mips.REGTMP
p2.To.Type = obj.TYPE_MEM
p2.To.Reg = v.Args[0].Reg()
p3 := s.Prog(mips.ABEQ)
p3.From.Type = obj.TYPE_REG
p3.From.Reg = mips.REGTMP
p3.To.Type = obj.TYPE_BRANCH
p3.To.SetTarget(p)
s.Prog(mips.ASYNC)
case ssaop.OpMIPSLoweredAtomicCas:
// MOVW $0, Rout
// SYNC
// LL (Rarg0), Rtmp
// BNE Rtmp, Rarg1, 4(PC)
// MOVW Rarg2, Rout
// SC Rout, (Rarg0)
// BEQ Rout, -4(PC)
// SYNC
p := s.Prog(mips.AMOVW)
p.From.Type = obj.TYPE_REG
p.From.Reg = mips.REGZERO
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg0()
s.Prog(mips.ASYNC)
p1 := s.Prog(mips.ALL)
p1.From.Type = obj.TYPE_MEM
p1.From.Reg = v.Args[0].Reg()
p1.To.Type = obj.TYPE_REG
p1.To.Reg = mips.REGTMP
p2 := s.Prog(mips.ABNE)
p2.From.Type = obj.TYPE_REG
p2.From.Reg = v.Args[1].Reg()
p2.Reg = mips.REGTMP
p2.To.Type = obj.TYPE_BRANCH
p3 := s.Prog(mips.AMOVW)
p3.From.Type = obj.TYPE_REG
p3.From.Reg = v.Args[2].Reg()
p3.To.Type = obj.TYPE_REG
p3.To.Reg = v.Reg0()
p4 := s.Prog(mips.ASC)
p4.From.Type = obj.TYPE_REG
p4.From.Reg = v.Reg0()
p4.To.Type = obj.TYPE_MEM
p4.To.Reg = v.Args[0].Reg()
p5 := s.Prog(mips.ABEQ)
p5.From.Type = obj.TYPE_REG
p5.From.Reg = v.Reg0()
p5.To.Type = obj.TYPE_BRANCH
p5.To.SetTarget(p1)
s.Prog(mips.ASYNC)
p6 := s.Prog(obj.ANOP)
p2.To.SetTarget(p6)
case ssaop.OpMIPSLoweredNilCheck:
// Issue a load which will fault if arg is nil.
p := s.Prog(mips.AMOVB)
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
ssagen.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = mips.REGTMP
if logopt.Enabled() {
logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
}
if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
base.WarnfAt(v.Pos, "generated nil check")
}
case ssaop.OpMIPSFPFlagTrue,
ssaop.OpMIPSFPFlagFalse:
// MOVW $1, r
// CMOVF R0, r
cmov := mips.ACMOVF
if v.Op == ssaop.OpMIPSFPFlagFalse {
cmov = mips.ACMOVT
}
p := s.Prog(mips.AMOVW)
p.From.Type = obj.TYPE_CONST
p.From.Offset = 1
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
p1 := s.Prog(cmov)
p1.From.Type = obj.TYPE_REG
p1.From.Reg = mips.REGZERO
p1.To.Type = obj.TYPE_REG
p1.To.Reg = v.Reg()
case ssaop.OpMIPSLoweredGetClosurePtr:
// Closure pointer is R22 (mips.REGCTXT).
ssagen.CheckLoweredGetClosurePtr(v)
case ssaop.OpMIPSLoweredGetCallerSP:
// caller's SP is FixedFrameSize below the address of the first arg
p := s.Prog(mips.AMOVW)
p.From.Type = obj.TYPE_ADDR
p.From.Offset = -base.Ctxt.Arch.FixedFrameSize
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssaop.OpMIPSLoweredGetCallerPC:
p := s.Prog(obj.AGETCALLERPC)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssaop.OpMIPSLoweredPubBarrier:
// SYNC
s.Prog(v.Op.Asm())
case ssaop.OpClobber, ssaop.OpClobberReg:
// TODO: implement for clobberdead experiment. Nop is ok for now.
default:
v.Fatalf("genValue not implemented: %s", v.LongString())
}
}
var blockJump = map[block.BlockKind]struct {
asm, invasm obj.As
}{
block.BlockMIPSEQ: {mips.ABEQ, mips.ABNE},
block.BlockMIPSNE: {mips.ABNE, mips.ABEQ},
block.BlockMIPSLTZ: {mips.ABLTZ, mips.ABGEZ},
block.BlockMIPSGEZ: {mips.ABGEZ, mips.ABLTZ},
block.BlockMIPSLEZ: {mips.ABLEZ, mips.ABGTZ},
block.BlockMIPSGTZ: {mips.ABGTZ, mips.ABLEZ},
block.BlockMIPSFPT: {mips.ABFPT, mips.ABFPF},
block.BlockMIPSFPF: {mips.ABFPF, mips.ABFPT},
}
func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
switch b.Kind {
case block.BlockPlain, block.BlockDefer:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
case block.BlockExit, block.BlockRetJmp:
case block.BlockRet:
s.Prog(obj.ARET)
case block.BlockMIPSEQ, block.BlockMIPSNE,
block.BlockMIPSLTZ, block.BlockMIPSGEZ,
block.BlockMIPSLEZ, block.BlockMIPSGTZ,
block.BlockMIPSFPT, block.BlockMIPSFPF:
jmp := blockJump[b.Kind]
var p *obj.Prog
switch next {
case b.Succs[0].Block():
p = s.Br(jmp.invasm, b.Succs[1].Block())
case b.Succs[1].Block():
p = s.Br(jmp.asm, b.Succs[0].Block())
default:
if b.Likely != ssa.BranchUnlikely {
p = s.Br(jmp.asm, b.Succs[0].Block())
s.Br(obj.AJMP, b.Succs[1].Block())
} else {
p = s.Br(jmp.invasm, b.Succs[1].Block())
s.Br(obj.AJMP, b.Succs[0].Block())
}
}
if !b.Controls[0].Type.IsFlags() {
p.From.Type = obj.TYPE_REG
p.From.Reg = b.Controls[0].Reg()
}
case block.BlockMIPSJUMPTABLE:
// SLL $2, Rarg0, Rtmp
// ADDU Rarg1, Rtmp
// MOVW (Rtmp), Rtmp
// JMP (Rtmp)
p := s.Prog(mips.ASLL)
p.From.Type = obj.TYPE_CONST
p.From.Offset = 2 // idx*4
p.Reg = b.Controls[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = mips.REGTMP
p1 := s.Prog(mips.AADDU)
p1.From.Type = obj.TYPE_REG
p1.From.Reg = b.Controls[1].Reg()
p1.To.Type = obj.TYPE_REG
p1.To.Reg = mips.REGTMP
p2 := s.Prog(mips.AMOVW)
p2.From.Type = obj.TYPE_MEM
p2.From.Reg = mips.REGTMP
p2.From.Offset = 0
p2.To.Type = obj.TYPE_REG
p2.To.Reg = mips.REGTMP
p3 := s.Prog(obj.AJMP)
p3.To.Type = obj.TYPE_MEM
p3.To.Reg = mips.REGTMP
// Save jump tables for later resolution of the target blocks.
s.JumpTables = append(s.JumpTables, b)
default:
b.Fatalf("branch not implemented: %s", b.LongString())
}
}