]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: add support for GOARCH=mips{,le}
authorVladimir Stefanovic <vladimir.stefanovic@imgtec.com>
Tue, 18 Oct 2016 21:50:44 +0000 (23:50 +0200)
committerBrad Fitzpatrick <bradfitz@golang.org>
Tue, 8 Nov 2016 19:53:45 +0000 (19:53 +0000)
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>
src/cmd/compile/internal/mips/galign.go [new file with mode: 0644]
src/cmd/compile/internal/mips/ggen.go [new file with mode: 0644]
src/cmd/compile/internal/mips/prog.go [new file with mode: 0644]
src/cmd/compile/internal/mips/ssa.go [new file with mode: 0644]

diff --git a/src/cmd/compile/internal/mips/galign.go b/src/cmd/compile/internal/mips/galign.go
new file mode 100644 (file)
index 0000000..39f5d2b
--- /dev/null
@@ -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 (file)
index 0000000..ec540f8
--- /dev/null
@@ -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 (file)
index 0000000..32805f0
--- /dev/null
@@ -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 (file)
index 0000000..aef1f90
--- /dev/null
@@ -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())
+       }
+}