aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladimir Stefanovic <vladimir.stefanovic@imgtec.com>2016-10-18 23:50:44 +0200
committerBrad Fitzpatrick <bradfitz@golang.org>2016-11-08 19:53:45 +0000
commitf4c997578ad962074b8e3aaf28dcfaa2f48a8593 (patch)
tree8f4fda93784e858f6bd2b5b48b3d6eddb3e2b2fe
parent247fc4a98ee7e41bc317ba2245516f279927ae65 (diff)
downloadgo-f4c997578ad962074b8e3aaf28dcfaa2f48a8593.tar.gz
go-f4c997578ad962074b8e3aaf28dcfaa2f48a8593.zip
cmd/compile: add support for GOARCH=mips{,le}
Change-Id: Ib489dc847787aaab7ba1be96792f885469e346ae Reviewed-on: https://go-review.googlesource.com/31479 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com>
-rw-r--r--src/cmd/compile/internal/mips/galign.go26
-rw-r--r--src/cmd/compile/internal/mips/ggen.go101
-rw-r--r--src/cmd/compile/internal/mips/prog.go157
-rw-r--r--src/cmd/compile/internal/mips/ssa.go907
4 files changed, 1191 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/mips/galign.go b/src/cmd/compile/internal/mips/galign.go
new file mode 100644
index 0000000000..39f5d2bf64
--- /dev/null
+++ b/src/cmd/compile/internal/mips/galign.go
@@ -0,0 +1,26 @@
+// 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 (
+ "cmd/compile/internal/gc"
+ "cmd/compile/internal/ssa"
+ "cmd/internal/obj"
+ "cmd/internal/obj/mips"
+)
+
+func Init() {
+ gc.Thearch.LinkArch = &mips.Linkmips
+ if obj.GOARCH == "mipsle" {
+ gc.Thearch.LinkArch = &mips.Linkmipsle
+ }
+ gc.Thearch.REGSP = mips.REGSP
+ gc.Thearch.MAXWIDTH = (1 << 31) - 1
+ gc.Thearch.Defframe = defframe
+ gc.Thearch.Proginfo = proginfo
+ gc.Thearch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {}
+ gc.Thearch.SSAGenValue = ssaGenValue
+ gc.Thearch.SSAGenBlock = ssaGenBlock
+}
diff --git a/src/cmd/compile/internal/mips/ggen.go b/src/cmd/compile/internal/mips/ggen.go
new file mode 100644
index 0000000000..ec540f8102
--- /dev/null
+++ b/src/cmd/compile/internal/mips/ggen.go
@@ -0,0 +1,101 @@
+// 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 (
+ "cmd/compile/internal/gc"
+ "cmd/internal/obj"
+ "cmd/internal/obj/mips"
+)
+
+func defframe(ptxt *obj.Prog) {
+ // fill in argument size, stack size
+ ptxt.To.Type = obj.TYPE_TEXTSIZE
+
+ ptxt.To.Val = int32(gc.Rnd(gc.Curfn.Type.ArgWidth(), int64(gc.Widthptr)))
+ frame := uint32(gc.Rnd(gc.Stksize+gc.Maxarg, int64(gc.Widthreg)))
+ ptxt.To.Offset = int64(frame)
+
+ // insert code to zero ambiguously live variables
+ // so that the garbage collector only sees initialized values
+ // when it looks for pointers.
+ p := ptxt
+
+ hi := int64(0)
+ lo := hi
+
+ // iterate through declarations - they are sorted in decreasing xoffset order.
+ for _, n := range gc.Curfn.Func.Dcl {
+ if !n.Name.Needzero {
+ continue
+ }
+ if n.Class != gc.PAUTO {
+ gc.Fatalf("needzero class %d", n.Class)
+ }
+ if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 {
+ gc.Fatalf("var %L has size %d offset %d", n, int(n.Type.Width), int(n.Xoffset))
+ }
+
+ if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthreg) {
+ // merge with range we already have
+ lo = n.Xoffset
+
+ continue
+ }
+
+ // zero old range
+ p = zerorange(p, int64(frame), lo, hi)
+
+ // set new range
+ hi = n.Xoffset + n.Type.Width
+
+ lo = n.Xoffset
+ }
+
+ // zero final range
+ zerorange(p, int64(frame), lo, hi)
+}
+
+// TODO(mips): implement DUFFZERO
+func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
+
+ cnt := hi - lo
+ if cnt == 0 {
+ return p
+ }
+ if cnt < int64(4*gc.Widthptr) {
+ for i := int64(0); i < cnt; i += int64(gc.Widthptr) {
+ p = gc.Appendpp(p, mips.AMOVW, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGSP, gc.Ctxt.FixedFrameSize()+frame+lo+i)
+ }
+ } else {
+ //fmt.Printf("zerorange frame:%v, lo: %v, hi:%v \n", frame ,lo, hi)
+ // ADD $(FIXED_FRAME+frame+lo-4), SP, r1
+ // ADD $cnt, r1, r2
+ // loop:
+ // MOVW R0, (Widthptr)r1
+ // ADD $Widthptr, r1
+ // BNE r1, r2, loop
+ p = gc.Appendpp(p, mips.AADD, obj.TYPE_CONST, 0, gc.Ctxt.FixedFrameSize()+frame+lo-4, obj.TYPE_REG, mips.REGRT1, 0)
+ p.Reg = mips.REGSP
+ p = gc.Appendpp(p, mips.AADD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, mips.REGRT2, 0)
+ p.Reg = mips.REGRT1
+ p = gc.Appendpp(p, mips.AMOVW, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGRT1, int64(gc.Widthptr))
+ p1 := p
+ p = gc.Appendpp(p, mips.AADD, obj.TYPE_CONST, 0, int64(gc.Widthptr), obj.TYPE_REG, mips.REGRT1, 0)
+ p = gc.Appendpp(p, mips.ABNE, obj.TYPE_REG, mips.REGRT1, 0, obj.TYPE_BRANCH, 0, 0)
+ p.Reg = mips.REGRT2
+ gc.Patch(p, p1)
+ }
+
+ return p
+}
+
+func ginsnop() {
+ p := gc.Prog(mips.ANOR)
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = mips.REG_R0
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = mips.REG_R0
+}
diff --git a/src/cmd/compile/internal/mips/prog.go b/src/cmd/compile/internal/mips/prog.go
new file mode 100644
index 0000000000..32805f0777
--- /dev/null
+++ b/src/cmd/compile/internal/mips/prog.go
@@ -0,0 +1,157 @@
+// 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 (
+ "cmd/compile/internal/gc"
+ "cmd/internal/obj"
+ "cmd/internal/obj/mips"
+)
+
+const (
+ LeftRdwr uint32 = gc.LeftRead | gc.LeftWrite
+ RightRdwr uint32 = gc.RightRead | gc.RightWrite
+)
+
+// This table gives the basic information about instruction
+// generated by the compiler and processed in the optimizer.
+// See opt.h for bit definitions.
+//
+// Instructions not generated need not be listed.
+// As an exception to that rule, we typically write down all the
+// size variants of an operation even if we just use a subset.
+//
+// The table is formatted for 8-space tabs.
+var progtable = [mips.ALAST & obj.AMask]gc.ProgInfo{
+ obj.ATYPE: {Flags: gc.Pseudo | gc.Skip},
+ obj.ATEXT: {Flags: gc.Pseudo},
+ obj.AFUNCDATA: {Flags: gc.Pseudo},
+ obj.APCDATA: {Flags: gc.Pseudo},
+ obj.AUNDEF: {Flags: gc.Break},
+ obj.AUSEFIELD: {Flags: gc.OK},
+ obj.AVARDEF: {Flags: gc.Pseudo | gc.RightWrite},
+ obj.AVARKILL: {Flags: gc.Pseudo | gc.RightWrite},
+ obj.AVARLIVE: {Flags: gc.Pseudo | gc.LeftRead},
+
+ // NOP is an internal no-op that also stands
+ // for USED and SET annotations, not the MIPS opcode.
+ obj.ANOP: {Flags: gc.LeftRead | gc.RightWrite},
+
+ // Integer
+ mips.AADD & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ mips.AADDU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ mips.ASUB & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ mips.ASUBU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ mips.AAND & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ mips.AOR & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ mips.AXOR & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ mips.ANOR & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ mips.AMUL & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead},
+ mips.AMULU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead},
+ mips.ADIV & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead},
+ mips.ADIVU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead},
+ mips.ASLL & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ mips.ASRA & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ mips.ASRL & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ mips.ASGT & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ mips.ASGTU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+
+ mips.ACLZ & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightRead},
+ mips.ACLO & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightRead},
+
+ // Floating point.
+ mips.AADDF & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ mips.AADDD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ mips.ASUBF & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ mips.ASUBD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ mips.AMULF & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ mips.AMULD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ mips.ADIVF & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ mips.ADIVD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite},
+ mips.AABSF & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite},
+ mips.AABSD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite},
+ mips.ANEGF & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite},
+ mips.ANEGD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite},
+ mips.ACMPEQF & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RegRead},
+ mips.ACMPEQD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RegRead},
+ mips.ACMPGTF & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RegRead},
+ mips.ACMPGTD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RegRead},
+ mips.ACMPGEF & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RegRead},
+ mips.ACMPGED & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RegRead},
+ mips.AMOVFD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv},
+ mips.AMOVDF & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv},
+ mips.AMOVFW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv},
+ mips.AMOVWF & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv},
+ mips.AMOVDW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv},
+ mips.AMOVWD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv},
+ mips.ATRUNCFW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv},
+ mips.ATRUNCDW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv},
+
+ mips.ASQRTF & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite},
+ mips.ASQRTD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite},
+
+ // Moves
+ mips.AMOVB & obj.AMask: {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+ mips.AMOVBU & obj.AMask: {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+ mips.AMOVH & obj.AMask: {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+ mips.AMOVHU & obj.AMask: {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+ mips.AMOVW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+ mips.AMOVF & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+ mips.AMOVD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+
+ // Conditional moves
+ mips.ACMOVN & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | RightRdwr},
+ mips.ACMOVZ & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | RightRdwr},
+ mips.ACMOVT & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | RightRdwr},
+ mips.ACMOVF & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | RightRdwr},
+
+ // Conditional trap
+ mips.ATEQ & obj.AMask: {Flags: gc.SizeL | gc.RegRead | gc.RightRead},
+ mips.ATNE & obj.AMask: {Flags: gc.SizeL | gc.RegRead | gc.RightRead},
+
+ // Atomic
+ mips.ASYNC & obj.AMask: {Flags: gc.OK},
+ mips.ALL & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite},
+ mips.ASC & obj.AMask: {Flags: gc.SizeL | LeftRdwr | gc.RightRead},
+
+ // Jumps
+ mips.AJMP & obj.AMask: {Flags: gc.Jump | gc.Break},
+ mips.AJAL & obj.AMask: {Flags: gc.Call},
+ mips.ABEQ & obj.AMask: {Flags: gc.Cjmp},
+ mips.ABNE & obj.AMask: {Flags: gc.Cjmp},
+ mips.ABGEZ & obj.AMask: {Flags: gc.Cjmp},
+ mips.ABLTZ & obj.AMask: {Flags: gc.Cjmp},
+ mips.ABGTZ & obj.AMask: {Flags: gc.Cjmp},
+ mips.ABLEZ & obj.AMask: {Flags: gc.Cjmp},
+ mips.ABFPF & obj.AMask: {Flags: gc.Cjmp},
+ mips.ABFPT & obj.AMask: {Flags: gc.Cjmp},
+ mips.ARET & obj.AMask: {Flags: gc.Break},
+ obj.ADUFFZERO: {Flags: gc.Call},
+ obj.ADUFFCOPY: {Flags: gc.Call},
+}
+
+func proginfo(p *obj.Prog) gc.ProgInfo {
+ info := progtable[p.As&obj.AMask]
+
+ if info.Flags == 0 {
+ gc.Fatalf("proginfo: unknown instruction %v", p)
+ }
+
+ if (info.Flags&gc.RegRead != 0) && p.Reg == 0 {
+ info.Flags &^= gc.RegRead
+ info.Flags |= gc.RightRead
+ }
+
+ if p.From.Type == obj.TYPE_ADDR && p.From.Sym != nil && (info.Flags&gc.LeftRead != 0) {
+ info.Flags &^= gc.LeftRead
+ info.Flags |= gc.LeftAddr
+ }
+
+ if p.As == mips.AMUL && p.To.Reg != 0 {
+ info.Flags |= gc.RightWrite
+ }
+
+ return info
+}
diff --git a/src/cmd/compile/internal/mips/ssa.go b/src/cmd/compile/internal/mips/ssa.go
new file mode 100644
index 0000000000..aef1f90969
--- /dev/null
+++ b/src/cmd/compile/internal/mips/ssa.go
@@ -0,0 +1,907 @@
+// 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/gc"
+ "cmd/compile/internal/ssa"
+ "cmd/internal/obj"
+ "cmd/internal/obj/mips"
+)
+
+// isFPreg returns whether r is an FP register
+func isFPreg(r int16) bool {
+ return mips.REG_F0 <= r && r <= mips.REG_F31
+}
+
+// isHILO returns whether r is HI or LO register
+func isHILO(r int16) bool {
+ return r == mips.REG_HI || r == mips.REG_LO
+}
+
+// loadByType returns the load instruction of the given type.
+func loadByType(t ssa.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 ssa.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 *gc.SSAGenState, v *ssa.Value) {
+ s.SetLineno(v.Line)
+ switch v.Op {
+ case ssa.OpInitMem:
+ // memory arg needs no code
+ case ssa.OpArg:
+ // input args need no code
+ case ssa.OpSP, ssa.OpSB, ssa.OpGetG:
+ // nothing to do
+ case ssa.OpSelect0, ssa.OpSelect1:
+ // nothing to do
+ case ssa.OpCopy, ssa.OpMIPSMOVWconvert, ssa.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 := gc.Prog(as)
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = x
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = y
+ if isHILO(x) && isHILO(y) || isHILO(x) && isFPreg(y) || isFPreg(x) && isHILO(y) {
+ // cannot move between special registers, use TMP as intermediate
+ p.To.Reg = mips.REGTMP
+ p = gc.Prog(mips.AMOVW)
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = mips.REGTMP
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = y
+ }
+ case ssa.OpMIPSMOVWnop:
+ if v.Reg() != v.Args[0].Reg() {
+ v.Fatalf("input[0] and output not in same register %s", v.LongString())
+ }
+ // nothing to do
+ case ssa.OpLoadReg:
+ if v.Type.IsFlags() {
+ v.Fatalf("load flags not implemented: %v", v.LongString())
+ return
+ }
+ r := v.Reg()
+ p := gc.Prog(loadByType(v.Type, r))
+ gc.AddrAuto(&p.From, v.Args[0])
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = r
+ if isHILO(r) {
+ // cannot directly load, load to TMP and move
+ p.To.Reg = mips.REGTMP
+ p = gc.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 ssa.OpStoreReg:
+ if v.Type.IsFlags() {
+ v.Fatalf("store flags not implemented: %v", v.LongString())
+ return
+ }
+ r := v.Args[0].Reg()
+ if isHILO(r) {
+ // cannot directly store, move to TMP and store
+ p := gc.Prog(mips.AMOVW)
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = r
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = mips.REGTMP
+ r = mips.REGTMP
+ }
+ p := gc.Prog(storeByType(v.Type, r))
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = r
+ gc.AddrAuto(&p.To, v)
+ case ssa.OpMIPSADD,
+ ssa.OpMIPSSUB,
+ ssa.OpMIPSAND,
+ ssa.OpMIPSOR,
+ ssa.OpMIPSXOR,
+ ssa.OpMIPSNOR,
+ ssa.OpMIPSSLL,
+ ssa.OpMIPSSRL,
+ ssa.OpMIPSSRA,
+ ssa.OpMIPSADDF,
+ ssa.OpMIPSADDD,
+ ssa.OpMIPSSUBF,
+ ssa.OpMIPSSUBD,
+ ssa.OpMIPSMULF,
+ ssa.OpMIPSMULD,
+ ssa.OpMIPSDIVF,
+ ssa.OpMIPSDIVD,
+ ssa.OpMIPSMUL:
+ p := gc.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 ssa.OpMIPSSGT,
+ ssa.OpMIPSSGTU:
+ p := gc.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 ssa.OpMIPSSGTzero,
+ ssa.OpMIPSSGTUzero:
+ p := gc.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 ssa.OpMIPSADDconst,
+ ssa.OpMIPSSUBconst,
+ ssa.OpMIPSANDconst,
+ ssa.OpMIPSORconst,
+ ssa.OpMIPSXORconst,
+ ssa.OpMIPSNORconst,
+ ssa.OpMIPSSLLconst,
+ ssa.OpMIPSSRLconst,
+ ssa.OpMIPSSRAconst,
+ ssa.OpMIPSSGTconst,
+ ssa.OpMIPSSGTUconst:
+ p := gc.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 ssa.OpMIPSMULT,
+ ssa.OpMIPSMULTU,
+ ssa.OpMIPSDIV,
+ ssa.OpMIPSDIVU:
+ // result in hi,lo
+ p := gc.Prog(v.Op.Asm())
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = v.Args[1].Reg()
+ p.Reg = v.Args[0].Reg()
+ case ssa.OpMIPSMOVWconst:
+ r := v.Reg()
+ 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
+ if isFPreg(r) || isHILO(r) {
+ // cannot move into FP or special registers, use TMP as intermediate
+ p.To.Reg = mips.REGTMP
+ p = gc.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 ssa.OpMIPSMOVFconst,
+ ssa.OpMIPSMOVDconst:
+ 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 = v.Reg()
+ case ssa.OpMIPSCMOVZ:
+ if v.Reg() != v.Args[0].Reg() {
+ 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 = v.Args[2].Reg()
+ p.Reg = v.Args[1].Reg()
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = v.Reg()
+ case ssa.OpMIPSCMOVZzero:
+ if v.Reg() != v.Args[0].Reg() {
+ 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 = v.Args[1].Reg()
+ p.Reg = mips.REGZERO
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = v.Reg()
+ case ssa.OpMIPSCMPEQF,
+ ssa.OpMIPSCMPEQD,
+ ssa.OpMIPSCMPGEF,
+ ssa.OpMIPSCMPGED,
+ ssa.OpMIPSCMPGTF,
+ ssa.OpMIPSCMPGTD:
+ p := gc.Prog(v.Op.Asm())
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = v.Args[0].Reg()
+ p.Reg = v.Args[1].Reg()
+ case ssa.OpMIPSMOVWaddr:
+ p := gc.Prog(mips.AMOVW)
+ p.From.Type = obj.TYPE_ADDR
+ 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 *ssa.ExternSymbol:
+ wantreg = "SB"
+ gc.AddAux(&p.From, v)
+ case *ssa.ArgSymbol, *ssa.AutoSymbol:
+ wantreg = "SP"
+ gc.AddAux(&p.From, v)
+ case nil:
+ // No sym, just MOVW $off(SP), R
+ wantreg = "SP"
+ p.From.Reg = mips.REGSP
+ 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 ssa.OpMIPSMOVBload,
+ ssa.OpMIPSMOVBUload,
+ ssa.OpMIPSMOVHload,
+ ssa.OpMIPSMOVHUload,
+ ssa.OpMIPSMOVWload,
+ ssa.OpMIPSMOVFload,
+ ssa.OpMIPSMOVDload:
+ p := gc.Prog(v.Op.Asm())
+ p.From.Type = obj.TYPE_MEM
+ p.From.Reg = v.Args[0].Reg()
+ gc.AddAux(&p.From, v)
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = v.Reg()
+ case ssa.OpMIPSMOVBstore,
+ ssa.OpMIPSMOVHstore,
+ ssa.OpMIPSMOVWstore,
+ ssa.OpMIPSMOVFstore,
+ ssa.OpMIPSMOVDstore:
+ p := gc.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()
+ gc.AddAux(&p.To, v)
+ case ssa.OpMIPSMOVBstorezero,
+ ssa.OpMIPSMOVHstorezero,
+ ssa.OpMIPSMOVWstorezero:
+ p := gc.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()
+ gc.AddAux(&p.To, v)
+ case ssa.OpMIPSMOVBreg,
+ ssa.OpMIPSMOVBUreg,
+ ssa.OpMIPSMOVHreg,
+ ssa.OpMIPSMOVHUreg:
+ a := v.Args[0]
+ for a.Op == ssa.OpCopy || a.Op == ssa.OpMIPSMOVWreg || a.Op == ssa.OpMIPSMOVWnop {
+ a = a.Args[0]
+ }
+ if a.Op == ssa.OpLoadReg {
+ t := a.Type
+ switch {
+ case v.Op == ssa.OpMIPSMOVBreg && t.Size() == 1 && t.IsSigned(),
+ v.Op == ssa.OpMIPSMOVBUreg && t.Size() == 1 && !t.IsSigned(),
+ v.Op == ssa.OpMIPSMOVHreg && t.Size() == 2 && t.IsSigned(),
+ v.Op == ssa.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 := gc.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 ssa.OpMIPSMOVWF,
+ ssa.OpMIPSMOVWD,
+ ssa.OpMIPSTRUNCFW,
+ ssa.OpMIPSTRUNCDW,
+ ssa.OpMIPSMOVFD,
+ ssa.OpMIPSMOVDF,
+ ssa.OpMIPSNEGF,
+ ssa.OpMIPSNEGD,
+ ssa.OpMIPSSQRTD,
+ ssa.OpMIPSCLZ:
+ p := gc.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 ssa.OpMIPSNEG:
+ // SUB from REGZERO
+ p := gc.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 ssa.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 := gc.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 := gc.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 := gc.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 := gc.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
+ gc.Patch(p4, p2)
+ case ssa.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 := gc.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 := gc.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 := gc.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 := gc.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 := gc.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 := gc.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
+ gc.Patch(p6, p2)
+ case ssa.OpMIPSCALLstatic:
+ 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.OpMIPSCALLclosure:
+ p := gc.Prog(obj.ACALL)
+ p.To.Type = obj.TYPE_MEM
+ p.To.Offset = 0
+ p.To.Reg = v.Args[0].Reg()
+ if gc.Maxarg < v.AuxInt {
+ gc.Maxarg = v.AuxInt
+ }
+ case ssa.OpMIPSCALLdefer:
+ 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.OpMIPSCALLgo:
+ 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.OpMIPSCALLinter:
+ p := gc.Prog(obj.ACALL)
+ p.To.Type = obj.TYPE_MEM
+ p.To.Offset = 0
+ p.To.Reg = v.Args[0].Reg()
+ if gc.Maxarg < v.AuxInt {
+ gc.Maxarg = v.AuxInt
+ }
+ case ssa.OpMIPSLoweredAtomicLoad:
+ gc.Prog(mips.ASYNC)
+
+ p := gc.Prog(mips.AMOVW)
+ p.From.Type = obj.TYPE_MEM
+ p.From.Reg = v.Args[0].Reg()
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = v.Reg0()
+
+ gc.Prog(mips.ASYNC)
+ case ssa.OpMIPSLoweredAtomicStore:
+ gc.Prog(mips.ASYNC)
+
+ p := gc.Prog(mips.AMOVW)
+ 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()
+
+ gc.Prog(mips.ASYNC)
+ case ssa.OpMIPSLoweredAtomicStorezero:
+ gc.Prog(mips.ASYNC)
+
+ p := gc.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()
+
+ gc.Prog(mips.ASYNC)
+ case ssa.OpMIPSLoweredAtomicExchange:
+ // SYNC
+ // MOVW Rarg1, Rtmp
+ // LL (Rarg0), Rout
+ // SC Rtmp, (Rarg0)
+ // BEQ Rtmp, -3(PC)
+ // SYNC
+ gc.Prog(mips.ASYNC)
+
+ p := gc.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 := gc.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 := gc.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 := gc.Prog(mips.ABEQ)
+ p3.From.Type = obj.TYPE_REG
+ p3.From.Reg = mips.REGTMP
+ p3.To.Type = obj.TYPE_BRANCH
+ gc.Patch(p3, p)
+
+ gc.Prog(mips.ASYNC)
+ case ssa.OpMIPSLoweredAtomicAdd:
+ // SYNC
+ // LL (Rarg0), Rout
+ // ADDU Rarg1, Rout, Rtmp
+ // SC Rtmp, (Rarg0)
+ // BEQ Rtmp, -3(PC)
+ // SYNC
+ // ADDU Rarg1, Rout
+ gc.Prog(mips.ASYNC)
+
+ p := gc.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 := gc.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 := gc.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 := gc.Prog(mips.ABEQ)
+ p3.From.Type = obj.TYPE_REG
+ p3.From.Reg = mips.REGTMP
+ p3.To.Type = obj.TYPE_BRANCH
+ gc.Patch(p3, p)
+
+ gc.Prog(mips.ASYNC)
+
+ p4 := gc.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 ssa.OpMIPSLoweredAtomicAddconst:
+ // SYNC
+ // LL (Rarg0), Rout
+ // ADDU $auxInt, Rout, Rtmp
+ // SC Rtmp, (Rarg0)
+ // BEQ Rtmp, -3(PC)
+ // SYNC
+ // ADDU $auxInt, Rout
+ gc.Prog(mips.ASYNC)
+
+ p := gc.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 := gc.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 := gc.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 := gc.Prog(mips.ABEQ)
+ p3.From.Type = obj.TYPE_REG
+ p3.From.Reg = mips.REGTMP
+ p3.To.Type = obj.TYPE_BRANCH
+ gc.Patch(p3, p)
+
+ gc.Prog(mips.ASYNC)
+
+ p4 := gc.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 ssa.OpMIPSLoweredAtomicAnd,
+ ssa.OpMIPSLoweredAtomicOr:
+ // SYNC
+ // LL (Rarg0), Rtmp
+ // AND/OR Rarg1, Rtmp
+ // SC Rtmp, (Rarg0)
+ // BEQ Rtmp, -3(PC)
+ // SYNC
+ gc.Prog(mips.ASYNC)
+
+ p := gc.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 := gc.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 := gc.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 := gc.Prog(mips.ABEQ)
+ p3.From.Type = obj.TYPE_REG
+ p3.From.Reg = mips.REGTMP
+ p3.To.Type = obj.TYPE_BRANCH
+ gc.Patch(p3, p)
+
+ gc.Prog(mips.ASYNC)
+
+ case ssa.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 := gc.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()
+
+ gc.Prog(mips.ASYNC)
+
+ p1 := gc.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 := gc.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 := gc.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 := gc.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 := gc.Prog(mips.ABEQ)
+ p5.From.Type = obj.TYPE_REG
+ p5.From.Reg = v.Reg0()
+ p5.To.Type = obj.TYPE_BRANCH
+ gc.Patch(p5, p1)
+
+ gc.Prog(mips.ASYNC)
+
+ p6 := gc.Prog(obj.ANOP)
+ gc.Patch(p2, p6)
+
+ 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.OpPhi:
+ gc.CheckLoweredPhi(v)
+ case ssa.OpMIPSLoweredNilCheck:
+ // Issue a load which will fault if arg is nil.
+ p := gc.Prog(mips.AMOVB)
+ p.From.Type = obj.TYPE_MEM
+ p.From.Reg = v.Args[0].Reg()
+ gc.AddAux(&p.From, v)
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = mips.REGTMP
+ if gc.Debug_checknil != 0 && v.Line > 1 { // v.Line==1 in generated wrappers
+ gc.Warnl(v.Line, "generated nil check")
+ }
+ case ssa.OpMIPSFPFlagTrue,
+ ssa.OpMIPSFPFlagFalse:
+ // MOVW $1, r
+ // CMOVF R0, r
+
+ cmov := mips.ACMOVF
+ if v.Op == ssa.OpMIPSFPFlagFalse {
+ cmov = mips.ACMOVT
+ }
+ p := gc.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 := gc.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 ssa.OpMIPSLoweredGetClosurePtr:
+ // Closure pointer is R22 (mips.REGCTXT).
+ gc.CheckLoweredGetClosurePtr(v)
+ default:
+ v.Fatalf("genValue not implemented: %s", v.LongString())
+ }
+}
+
+var blockJump = map[ssa.BlockKind]struct {
+ asm, invasm obj.As
+}{
+ ssa.BlockMIPSEQ: {mips.ABEQ, mips.ABNE},
+ ssa.BlockMIPSNE: {mips.ABNE, mips.ABEQ},
+ ssa.BlockMIPSLTZ: {mips.ABLTZ, mips.ABGEZ},
+ ssa.BlockMIPSGEZ: {mips.ABGEZ, mips.ABLTZ},
+ ssa.BlockMIPSLEZ: {mips.ABLEZ, mips.ABGTZ},
+ ssa.BlockMIPSGTZ: {mips.ABGTZ, mips.ABLEZ},
+ ssa.BlockMIPSFPT: {mips.ABFPT, mips.ABFPF},
+ ssa.BlockMIPSFPF: {mips.ABFPF, mips.ABFPT},
+}
+
+func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
+ s.SetLineno(b.Line)
+
+ switch b.Kind {
+ case ssa.BlockPlain:
+ if b.Succs[0].Block() != next {
+ p := gc.Prog(obj.AJMP)
+ 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 R1:
+ // 0 if we should continue executing
+ // 1 if we should jump to deferreturn call
+ p := gc.Prog(mips.ABNE)
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = mips.REGZERO
+ p.Reg = mips.REG_R1
+ 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(obj.AJMP)
+ 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(obj.ARET)
+ p.To.Type = obj.TYPE_MEM
+ p.To.Name = obj.NAME_EXTERN
+ p.To.Sym = gc.Linksym(b.Aux.(*gc.Sym))
+ case ssa.BlockMIPSEQ, ssa.BlockMIPSNE,
+ ssa.BlockMIPSLTZ, ssa.BlockMIPSGEZ,
+ ssa.BlockMIPSLEZ, ssa.BlockMIPSGTZ,
+ ssa.BlockMIPSFPT, ssa.BlockMIPSFPF:
+ jmp := blockJump[b.Kind]
+ var p *obj.Prog
+ switch next {
+ case b.Succs[0].Block():
+ p = gc.Prog(jmp.invasm)
+ 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(obj.AJMP)
+ q.To.Type = obj.TYPE_BRANCH
+ s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()})
+ }
+ if !b.Control.Type.IsFlags() {
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = b.Control.Reg()
+ }
+ default:
+ b.Fatalf("branch not implemented: %s. Control: %s", b.LongString(), b.Control.LongString())
+ }
+}