Home | History | Annotate | Download | only in mips
      1 // Copyright 2016 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package mips
      6 
      7 import (
      8 	"math"
      9 
     10 	"cmd/compile/internal/gc"
     11 	"cmd/compile/internal/ssa"
     12 	"cmd/internal/obj"
     13 	"cmd/internal/obj/mips"
     14 )
     15 
     16 // isFPreg returns whether r is an FP register
     17 func isFPreg(r int16) bool {
     18 	return mips.REG_F0 <= r && r <= mips.REG_F31
     19 }
     20 
     21 // isHILO returns whether r is HI or LO register
     22 func isHILO(r int16) bool {
     23 	return r == mips.REG_HI || r == mips.REG_LO
     24 }
     25 
     26 // loadByType returns the load instruction of the given type.
     27 func loadByType(t ssa.Type, r int16) obj.As {
     28 	if isFPreg(r) {
     29 		if t.Size() == 4 { // float32 or int32
     30 			return mips.AMOVF
     31 		} else { // float64 or int64
     32 			return mips.AMOVD
     33 		}
     34 	} else {
     35 		switch t.Size() {
     36 		case 1:
     37 			if t.IsSigned() {
     38 				return mips.AMOVB
     39 			} else {
     40 				return mips.AMOVBU
     41 			}
     42 		case 2:
     43 			if t.IsSigned() {
     44 				return mips.AMOVH
     45 			} else {
     46 				return mips.AMOVHU
     47 			}
     48 		case 4:
     49 			return mips.AMOVW
     50 		}
     51 	}
     52 	panic("bad load type")
     53 }
     54 
     55 // storeByType returns the store instruction of the given type.
     56 func storeByType(t ssa.Type, r int16) obj.As {
     57 	if isFPreg(r) {
     58 		if t.Size() == 4 { // float32 or int32
     59 			return mips.AMOVF
     60 		} else { // float64 or int64
     61 			return mips.AMOVD
     62 		}
     63 	} else {
     64 		switch t.Size() {
     65 		case 1:
     66 			return mips.AMOVB
     67 		case 2:
     68 			return mips.AMOVH
     69 		case 4:
     70 			return mips.AMOVW
     71 		}
     72 	}
     73 	panic("bad store type")
     74 }
     75 
     76 func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
     77 	s.SetLineno(v.Line)
     78 	switch v.Op {
     79 	case ssa.OpInitMem:
     80 		// memory arg needs no code
     81 	case ssa.OpArg:
     82 		// input args need no code
     83 	case ssa.OpSP, ssa.OpSB, ssa.OpGetG:
     84 		// nothing to do
     85 	case ssa.OpSelect0, ssa.OpSelect1:
     86 		// nothing to do
     87 	case ssa.OpCopy, ssa.OpMIPSMOVWconvert, ssa.OpMIPSMOVWreg:
     88 		t := v.Type
     89 		if t.IsMemory() {
     90 			return
     91 		}
     92 		x := v.Args[0].Reg()
     93 		y := v.Reg()
     94 		if x == y {
     95 			return
     96 		}
     97 		as := mips.AMOVW
     98 		if isFPreg(x) && isFPreg(y) {
     99 			as = mips.AMOVF
    100 			if t.Size() == 8 {
    101 				as = mips.AMOVD
    102 			}
    103 		}
    104 
    105 		p := gc.Prog(as)
    106 		p.From.Type = obj.TYPE_REG
    107 		p.From.Reg = x
    108 		p.To.Type = obj.TYPE_REG
    109 		p.To.Reg = y
    110 		if isHILO(x) && isHILO(y) || isHILO(x) && isFPreg(y) || isFPreg(x) && isHILO(y) {
    111 			// cannot move between special registers, use TMP as intermediate
    112 			p.To.Reg = mips.REGTMP
    113 			p = gc.Prog(mips.AMOVW)
    114 			p.From.Type = obj.TYPE_REG
    115 			p.From.Reg = mips.REGTMP
    116 			p.To.Type = obj.TYPE_REG
    117 			p.To.Reg = y
    118 		}
    119 	case ssa.OpMIPSMOVWnop:
    120 		if v.Reg() != v.Args[0].Reg() {
    121 			v.Fatalf("input[0] and output not in same register %s", v.LongString())
    122 		}
    123 		// nothing to do
    124 	case ssa.OpLoadReg:
    125 		if v.Type.IsFlags() {
    126 			v.Fatalf("load flags not implemented: %v", v.LongString())
    127 			return
    128 		}
    129 		r := v.Reg()
    130 		p := gc.Prog(loadByType(v.Type, r))
    131 		gc.AddrAuto(&p.From, v.Args[0])
    132 		p.To.Type = obj.TYPE_REG
    133 		p.To.Reg = r
    134 		if isHILO(r) {
    135 			// cannot directly load, load to TMP and move
    136 			p.To.Reg = mips.REGTMP
    137 			p = gc.Prog(mips.AMOVW)
    138 			p.From.Type = obj.TYPE_REG
    139 			p.From.Reg = mips.REGTMP
    140 			p.To.Type = obj.TYPE_REG
    141 			p.To.Reg = r
    142 		}
    143 	case ssa.OpStoreReg:
    144 		if v.Type.IsFlags() {
    145 			v.Fatalf("store flags not implemented: %v", v.LongString())
    146 			return
    147 		}
    148 		r := v.Args[0].Reg()
    149 		if isHILO(r) {
    150 			// cannot directly store, move to TMP and store
    151 			p := gc.Prog(mips.AMOVW)
    152 			p.From.Type = obj.TYPE_REG
    153 			p.From.Reg = r
    154 			p.To.Type = obj.TYPE_REG
    155 			p.To.Reg = mips.REGTMP
    156 			r = mips.REGTMP
    157 		}
    158 		p := gc.Prog(storeByType(v.Type, r))
    159 		p.From.Type = obj.TYPE_REG
    160 		p.From.Reg = r
    161 		gc.AddrAuto(&p.To, v)
    162 	case ssa.OpMIPSADD,
    163 		ssa.OpMIPSSUB,
    164 		ssa.OpMIPSAND,
    165 		ssa.OpMIPSOR,
    166 		ssa.OpMIPSXOR,
    167 		ssa.OpMIPSNOR,
    168 		ssa.OpMIPSSLL,
    169 		ssa.OpMIPSSRL,
    170 		ssa.OpMIPSSRA,
    171 		ssa.OpMIPSADDF,
    172 		ssa.OpMIPSADDD,
    173 		ssa.OpMIPSSUBF,
    174 		ssa.OpMIPSSUBD,
    175 		ssa.OpMIPSMULF,
    176 		ssa.OpMIPSMULD,
    177 		ssa.OpMIPSDIVF,
    178 		ssa.OpMIPSDIVD,
    179 		ssa.OpMIPSMUL:
    180 		p := gc.Prog(v.Op.Asm())
    181 		p.From.Type = obj.TYPE_REG
    182 		p.From.Reg = v.Args[1].Reg()
    183 		p.Reg = v.Args[0].Reg()
    184 		p.To.Type = obj.TYPE_REG
    185 		p.To.Reg = v.Reg()
    186 	case ssa.OpMIPSSGT,
    187 		ssa.OpMIPSSGTU:
    188 		p := gc.Prog(v.Op.Asm())
    189 		p.From.Type = obj.TYPE_REG
    190 		p.From.Reg = v.Args[0].Reg()
    191 		p.Reg = v.Args[1].Reg()
    192 		p.To.Type = obj.TYPE_REG
    193 		p.To.Reg = v.Reg()
    194 	case ssa.OpMIPSSGTzero,
    195 		ssa.OpMIPSSGTUzero:
    196 		p := gc.Prog(v.Op.Asm())
    197 		p.From.Type = obj.TYPE_REG
    198 		p.From.Reg = v.Args[0].Reg()
    199 		p.Reg = mips.REGZERO
    200 		p.To.Type = obj.TYPE_REG
    201 		p.To.Reg = v.Reg()
    202 	case ssa.OpMIPSADDconst,
    203 		ssa.OpMIPSSUBconst,
    204 		ssa.OpMIPSANDconst,
    205 		ssa.OpMIPSORconst,
    206 		ssa.OpMIPSXORconst,
    207 		ssa.OpMIPSNORconst,
    208 		ssa.OpMIPSSLLconst,
    209 		ssa.OpMIPSSRLconst,
    210 		ssa.OpMIPSSRAconst,
    211 		ssa.OpMIPSSGTconst,
    212 		ssa.OpMIPSSGTUconst:
    213 		p := gc.Prog(v.Op.Asm())
    214 		p.From.Type = obj.TYPE_CONST
    215 		p.From.Offset = v.AuxInt
    216 		p.Reg = v.Args[0].Reg()
    217 		p.To.Type = obj.TYPE_REG
    218 		p.To.Reg = v.Reg()
    219 	case ssa.OpMIPSMULT,
    220 		ssa.OpMIPSMULTU,
    221 		ssa.OpMIPSDIV,
    222 		ssa.OpMIPSDIVU:
    223 		// result in hi,lo
    224 		p := gc.Prog(v.Op.Asm())
    225 		p.From.Type = obj.TYPE_REG
    226 		p.From.Reg = v.Args[1].Reg()
    227 		p.Reg = v.Args[0].Reg()
    228 	case ssa.OpMIPSMOVWconst:
    229 		r := v.Reg()
    230 		p := gc.Prog(v.Op.Asm())
    231 		p.From.Type = obj.TYPE_CONST
    232 		p.From.Offset = v.AuxInt
    233 		p.To.Type = obj.TYPE_REG
    234 		p.To.Reg = r
    235 		if isFPreg(r) || isHILO(r) {
    236 			// cannot move into FP or special registers, use TMP as intermediate
    237 			p.To.Reg = mips.REGTMP
    238 			p = gc.Prog(mips.AMOVW)
    239 			p.From.Type = obj.TYPE_REG
    240 			p.From.Reg = mips.REGTMP
    241 			p.To.Type = obj.TYPE_REG
    242 			p.To.Reg = r
    243 		}
    244 	case ssa.OpMIPSMOVFconst,
    245 		ssa.OpMIPSMOVDconst:
    246 		p := gc.Prog(v.Op.Asm())
    247 		p.From.Type = obj.TYPE_FCONST
    248 		p.From.Val = math.Float64frombits(uint64(v.AuxInt))
    249 		p.To.Type = obj.TYPE_REG
    250 		p.To.Reg = v.Reg()
    251 	case ssa.OpMIPSCMOVZ:
    252 		if v.Reg() != v.Args[0].Reg() {
    253 			v.Fatalf("input[0] and output not in same register %s", v.LongString())
    254 		}
    255 		p := gc.Prog(v.Op.Asm())
    256 		p.From.Type = obj.TYPE_REG
    257 		p.From.Reg = v.Args[2].Reg()
    258 		p.Reg = v.Args[1].Reg()
    259 		p.To.Type = obj.TYPE_REG
    260 		p.To.Reg = v.Reg()
    261 	case ssa.OpMIPSCMOVZzero:
    262 		if v.Reg() != v.Args[0].Reg() {
    263 			v.Fatalf("input[0] and output not in same register %s", v.LongString())
    264 		}
    265 		p := gc.Prog(v.Op.Asm())
    266 		p.From.Type = obj.TYPE_REG
    267 		p.From.Reg = v.Args[1].Reg()
    268 		p.Reg = mips.REGZERO
    269 		p.To.Type = obj.TYPE_REG
    270 		p.To.Reg = v.Reg()
    271 	case ssa.OpMIPSCMPEQF,
    272 		ssa.OpMIPSCMPEQD,
    273 		ssa.OpMIPSCMPGEF,
    274 		ssa.OpMIPSCMPGED,
    275 		ssa.OpMIPSCMPGTF,
    276 		ssa.OpMIPSCMPGTD:
    277 		p := gc.Prog(v.Op.Asm())
    278 		p.From.Type = obj.TYPE_REG
    279 		p.From.Reg = v.Args[0].Reg()
    280 		p.Reg = v.Args[1].Reg()
    281 	case ssa.OpMIPSMOVWaddr:
    282 		p := gc.Prog(mips.AMOVW)
    283 		p.From.Type = obj.TYPE_ADDR
    284 		var wantreg string
    285 		// MOVW $sym+off(base), R
    286 		// the assembler expands it as the following:
    287 		// - base is SP: add constant offset to SP (R29)
    288 		//               when constant is large, tmp register (R23) may be used
    289 		// - base is SB: load external address with relocation
    290 		switch v.Aux.(type) {
    291 		default:
    292 			v.Fatalf("aux is of unknown type %T", v.Aux)
    293 		case *ssa.ExternSymbol:
    294 			wantreg = "SB"
    295 			gc.AddAux(&p.From, v)
    296 		case *ssa.ArgSymbol, *ssa.AutoSymbol:
    297 			wantreg = "SP"
    298 			gc.AddAux(&p.From, v)
    299 		case nil:
    300 			// No sym, just MOVW $off(SP), R
    301 			wantreg = "SP"
    302 			p.From.Reg = mips.REGSP
    303 			p.From.Offset = v.AuxInt
    304 		}
    305 		if reg := v.Args[0].RegName(); reg != wantreg {
    306 			v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg)
    307 		}
    308 		p.To.Type = obj.TYPE_REG
    309 		p.To.Reg = v.Reg()
    310 	case ssa.OpMIPSMOVBload,
    311 		ssa.OpMIPSMOVBUload,
    312 		ssa.OpMIPSMOVHload,
    313 		ssa.OpMIPSMOVHUload,
    314 		ssa.OpMIPSMOVWload,
    315 		ssa.OpMIPSMOVFload,
    316 		ssa.OpMIPSMOVDload:
    317 		p := gc.Prog(v.Op.Asm())
    318 		p.From.Type = obj.TYPE_MEM
    319 		p.From.Reg = v.Args[0].Reg()
    320 		gc.AddAux(&p.From, v)
    321 		p.To.Type = obj.TYPE_REG
    322 		p.To.Reg = v.Reg()
    323 	case ssa.OpMIPSMOVBstore,
    324 		ssa.OpMIPSMOVHstore,
    325 		ssa.OpMIPSMOVWstore,
    326 		ssa.OpMIPSMOVFstore,
    327 		ssa.OpMIPSMOVDstore:
    328 		p := gc.Prog(v.Op.Asm())
    329 		p.From.Type = obj.TYPE_REG
    330 		p.From.Reg = v.Args[1].Reg()
    331 		p.To.Type = obj.TYPE_MEM
    332 		p.To.Reg = v.Args[0].Reg()
    333 		gc.AddAux(&p.To, v)
    334 	case ssa.OpMIPSMOVBstorezero,
    335 		ssa.OpMIPSMOVHstorezero,
    336 		ssa.OpMIPSMOVWstorezero:
    337 		p := gc.Prog(v.Op.Asm())
    338 		p.From.Type = obj.TYPE_REG
    339 		p.From.Reg = mips.REGZERO
    340 		p.To.Type = obj.TYPE_MEM
    341 		p.To.Reg = v.Args[0].Reg()
    342 		gc.AddAux(&p.To, v)
    343 	case ssa.OpMIPSMOVBreg,
    344 		ssa.OpMIPSMOVBUreg,
    345 		ssa.OpMIPSMOVHreg,
    346 		ssa.OpMIPSMOVHUreg:
    347 		a := v.Args[0]
    348 		for a.Op == ssa.OpCopy || a.Op == ssa.OpMIPSMOVWreg || a.Op == ssa.OpMIPSMOVWnop {
    349 			a = a.Args[0]
    350 		}
    351 		if a.Op == ssa.OpLoadReg {
    352 			t := a.Type
    353 			switch {
    354 			case v.Op == ssa.OpMIPSMOVBreg && t.Size() == 1 && t.IsSigned(),
    355 				v.Op == ssa.OpMIPSMOVBUreg && t.Size() == 1 && !t.IsSigned(),
    356 				v.Op == ssa.OpMIPSMOVHreg && t.Size() == 2 && t.IsSigned(),
    357 				v.Op == ssa.OpMIPSMOVHUreg && t.Size() == 2 && !t.IsSigned():
    358 				// arg is a proper-typed load, already zero/sign-extended, don't extend again
    359 				if v.Reg() == v.Args[0].Reg() {
    360 					return
    361 				}
    362 				p := gc.Prog(mips.AMOVW)
    363 				p.From.Type = obj.TYPE_REG
    364 				p.From.Reg = v.Args[0].Reg()
    365 				p.To.Type = obj.TYPE_REG
    366 				p.To.Reg = v.Reg()
    367 				return
    368 			default:
    369 			}
    370 		}
    371 		fallthrough
    372 	case ssa.OpMIPSMOVWF,
    373 		ssa.OpMIPSMOVWD,
    374 		ssa.OpMIPSTRUNCFW,
    375 		ssa.OpMIPSTRUNCDW,
    376 		ssa.OpMIPSMOVFD,
    377 		ssa.OpMIPSMOVDF,
    378 		ssa.OpMIPSNEGF,
    379 		ssa.OpMIPSNEGD,
    380 		ssa.OpMIPSSQRTD,
    381 		ssa.OpMIPSCLZ:
    382 		p := gc.Prog(v.Op.Asm())
    383 		p.From.Type = obj.TYPE_REG
    384 		p.From.Reg = v.Args[0].Reg()
    385 		p.To.Type = obj.TYPE_REG
    386 		p.To.Reg = v.Reg()
    387 	case ssa.OpMIPSNEG:
    388 		// SUB from REGZERO
    389 		p := gc.Prog(mips.ASUBU)
    390 		p.From.Type = obj.TYPE_REG
    391 		p.From.Reg = v.Args[0].Reg()
    392 		p.Reg = mips.REGZERO
    393 		p.To.Type = obj.TYPE_REG
    394 		p.To.Reg = v.Reg()
    395 	case ssa.OpMIPSLoweredZero:
    396 		// SUBU	$4, R1
    397 		// MOVW	R0, 4(R1)
    398 		// ADDU	$4, R1
    399 		// BNE	Rarg1, R1, -2(PC)
    400 		// arg1 is the address of the last element to zero
    401 		var sz int64
    402 		var mov obj.As
    403 		switch {
    404 		case v.AuxInt%4 == 0:
    405 			sz = 4
    406 			mov = mips.AMOVW
    407 		case v.AuxInt%2 == 0:
    408 			sz = 2
    409 			mov = mips.AMOVH
    410 		default:
    411 			sz = 1
    412 			mov = mips.AMOVB
    413 		}
    414 		p := gc.Prog(mips.ASUBU)
    415 		p.From.Type = obj.TYPE_CONST
    416 		p.From.Offset = sz
    417 		p.To.Type = obj.TYPE_REG
    418 		p.To.Reg = mips.REG_R1
    419 		p2 := gc.Prog(mov)
    420 		p2.From.Type = obj.TYPE_REG
    421 		p2.From.Reg = mips.REGZERO
    422 		p2.To.Type = obj.TYPE_MEM
    423 		p2.To.Reg = mips.REG_R1
    424 		p2.To.Offset = sz
    425 		p3 := gc.Prog(mips.AADDU)
    426 		p3.From.Type = obj.TYPE_CONST
    427 		p3.From.Offset = sz
    428 		p3.To.Type = obj.TYPE_REG
    429 		p3.To.Reg = mips.REG_R1
    430 		p4 := gc.Prog(mips.ABNE)
    431 		p4.From.Type = obj.TYPE_REG
    432 		p4.From.Reg = v.Args[1].Reg()
    433 		p4.Reg = mips.REG_R1
    434 		p4.To.Type = obj.TYPE_BRANCH
    435 		gc.Patch(p4, p2)
    436 	case ssa.OpMIPSLoweredMove:
    437 		// SUBU	$4, R1
    438 		// MOVW	4(R1), Rtmp
    439 		// MOVW	Rtmp, (R2)
    440 		// ADDU	$4, R1
    441 		// ADDU	$4, R2
    442 		// BNE	Rarg2, R1, -4(PC)
    443 		// arg2 is the address of the last element of src
    444 		var sz int64
    445 		var mov obj.As
    446 		switch {
    447 		case v.AuxInt%4 == 0:
    448 			sz = 4
    449 			mov = mips.AMOVW
    450 		case v.AuxInt%2 == 0:
    451 			sz = 2
    452 			mov = mips.AMOVH
    453 		default:
    454 			sz = 1
    455 			mov = mips.AMOVB
    456 		}
    457 		p := gc.Prog(mips.ASUBU)
    458 		p.From.Type = obj.TYPE_CONST
    459 		p.From.Offset = sz
    460 		p.To.Type = obj.TYPE_REG
    461 		p.To.Reg = mips.REG_R1
    462 		p2 := gc.Prog(mov)
    463 		p2.From.Type = obj.TYPE_MEM
    464 		p2.From.Reg = mips.REG_R1
    465 		p2.From.Offset = sz
    466 		p2.To.Type = obj.TYPE_REG
    467 		p2.To.Reg = mips.REGTMP
    468 		p3 := gc.Prog(mov)
    469 		p3.From.Type = obj.TYPE_REG
    470 		p3.From.Reg = mips.REGTMP
    471 		p3.To.Type = obj.TYPE_MEM
    472 		p3.To.Reg = mips.REG_R2
    473 		p4 := gc.Prog(mips.AADDU)
    474 		p4.From.Type = obj.TYPE_CONST
    475 		p4.From.Offset = sz
    476 		p4.To.Type = obj.TYPE_REG
    477 		p4.To.Reg = mips.REG_R1
    478 		p5 := gc.Prog(mips.AADDU)
    479 		p5.From.Type = obj.TYPE_CONST
    480 		p5.From.Offset = sz
    481 		p5.To.Type = obj.TYPE_REG
    482 		p5.To.Reg = mips.REG_R2
    483 		p6 := gc.Prog(mips.ABNE)
    484 		p6.From.Type = obj.TYPE_REG
    485 		p6.From.Reg = v.Args[2].Reg()
    486 		p6.Reg = mips.REG_R1
    487 		p6.To.Type = obj.TYPE_BRANCH
    488 		gc.Patch(p6, p2)
    489 	case ssa.OpMIPSCALLstatic:
    490 		if v.Aux.(*gc.Sym) == gc.Deferreturn.Sym {
    491 			// Deferred calls will appear to be returning to
    492 			// the CALL deferreturn(SB) that we are about to emit.
    493 			// However, the stack trace code will show the line
    494 			// of the instruction byte before the return PC.
    495 			// To avoid that being an unrelated instruction,
    496 			// insert an actual hardware NOP that will have the right line number.
    497 			// This is different from obj.ANOP, which is a virtual no-op
    498 			// that doesn't make it into the instruction stream.
    499 			ginsnop()
    500 		}
    501 		p := gc.Prog(obj.ACALL)
    502 		p.To.Type = obj.TYPE_MEM
    503 		p.To.Name = obj.NAME_EXTERN
    504 		p.To.Sym = gc.Linksym(v.Aux.(*gc.Sym))
    505 		if gc.Maxarg < v.AuxInt {
    506 			gc.Maxarg = v.AuxInt
    507 		}
    508 	case ssa.OpMIPSCALLclosure:
    509 		p := gc.Prog(obj.ACALL)
    510 		p.To.Type = obj.TYPE_MEM
    511 		p.To.Offset = 0
    512 		p.To.Reg = v.Args[0].Reg()
    513 		if gc.Maxarg < v.AuxInt {
    514 			gc.Maxarg = v.AuxInt
    515 		}
    516 	case ssa.OpMIPSCALLdefer:
    517 		p := gc.Prog(obj.ACALL)
    518 		p.To.Type = obj.TYPE_MEM
    519 		p.To.Name = obj.NAME_EXTERN
    520 		p.To.Sym = gc.Linksym(gc.Deferproc.Sym)
    521 		if gc.Maxarg < v.AuxInt {
    522 			gc.Maxarg = v.AuxInt
    523 		}
    524 	case ssa.OpMIPSCALLgo:
    525 		p := gc.Prog(obj.ACALL)
    526 		p.To.Type = obj.TYPE_MEM
    527 		p.To.Name = obj.NAME_EXTERN
    528 		p.To.Sym = gc.Linksym(gc.Newproc.Sym)
    529 		if gc.Maxarg < v.AuxInt {
    530 			gc.Maxarg = v.AuxInt
    531 		}
    532 	case ssa.OpMIPSCALLinter:
    533 		p := gc.Prog(obj.ACALL)
    534 		p.To.Type = obj.TYPE_MEM
    535 		p.To.Offset = 0
    536 		p.To.Reg = v.Args[0].Reg()
    537 		if gc.Maxarg < v.AuxInt {
    538 			gc.Maxarg = v.AuxInt
    539 		}
    540 	case ssa.OpMIPSLoweredAtomicLoad:
    541 		gc.Prog(mips.ASYNC)
    542 
    543 		p := gc.Prog(mips.AMOVW)
    544 		p.From.Type = obj.TYPE_MEM
    545 		p.From.Reg = v.Args[0].Reg()
    546 		p.To.Type = obj.TYPE_REG
    547 		p.To.Reg = v.Reg0()
    548 
    549 		gc.Prog(mips.ASYNC)
    550 	case ssa.OpMIPSLoweredAtomicStore:
    551 		gc.Prog(mips.ASYNC)
    552 
    553 		p := gc.Prog(mips.AMOVW)
    554 		p.From.Type = obj.TYPE_REG
    555 		p.From.Reg = v.Args[1].Reg()
    556 		p.To.Type = obj.TYPE_MEM
    557 		p.To.Reg = v.Args[0].Reg()
    558 
    559 		gc.Prog(mips.ASYNC)
    560 	case ssa.OpMIPSLoweredAtomicStorezero:
    561 		gc.Prog(mips.ASYNC)
    562 
    563 		p := gc.Prog(mips.AMOVW)
    564 		p.From.Type = obj.TYPE_REG
    565 		p.From.Reg = mips.REGZERO
    566 		p.To.Type = obj.TYPE_MEM
    567 		p.To.Reg = v.Args[0].Reg()
    568 
    569 		gc.Prog(mips.ASYNC)
    570 	case ssa.OpMIPSLoweredAtomicExchange:
    571 		// SYNC
    572 		// MOVW Rarg1, Rtmp
    573 		// LL	(Rarg0), Rout
    574 		// SC	Rtmp, (Rarg0)
    575 		// BEQ	Rtmp, -3(PC)
    576 		// SYNC
    577 		gc.Prog(mips.ASYNC)
    578 
    579 		p := gc.Prog(mips.AMOVW)
    580 		p.From.Type = obj.TYPE_REG
    581 		p.From.Reg = v.Args[1].Reg()
    582 		p.To.Type = obj.TYPE_REG
    583 		p.To.Reg = mips.REGTMP
    584 
    585 		p1 := gc.Prog(mips.ALL)
    586 		p1.From.Type = obj.TYPE_MEM
    587 		p1.From.Reg = v.Args[0].Reg()
    588 		p1.To.Type = obj.TYPE_REG
    589 		p1.To.Reg = v.Reg0()
    590 
    591 		p2 := gc.Prog(mips.ASC)
    592 		p2.From.Type = obj.TYPE_REG
    593 		p2.From.Reg = mips.REGTMP
    594 		p2.To.Type = obj.TYPE_MEM
    595 		p2.To.Reg = v.Args[0].Reg()
    596 
    597 		p3 := gc.Prog(mips.ABEQ)
    598 		p3.From.Type = obj.TYPE_REG
    599 		p3.From.Reg = mips.REGTMP
    600 		p3.To.Type = obj.TYPE_BRANCH
    601 		gc.Patch(p3, p)
    602 
    603 		gc.Prog(mips.ASYNC)
    604 	case ssa.OpMIPSLoweredAtomicAdd:
    605 		// SYNC
    606 		// LL	(Rarg0), Rout
    607 		// ADDU Rarg1, Rout, Rtmp
    608 		// SC	Rtmp, (Rarg0)
    609 		// BEQ	Rtmp, -3(PC)
    610 		// SYNC
    611 		// ADDU Rarg1, Rout
    612 		gc.Prog(mips.ASYNC)
    613 
    614 		p := gc.Prog(mips.ALL)
    615 		p.From.Type = obj.TYPE_MEM
    616 		p.From.Reg = v.Args[0].Reg()
    617 		p.To.Type = obj.TYPE_REG
    618 		p.To.Reg = v.Reg0()
    619 
    620 		p1 := gc.Prog(mips.AADDU)
    621 		p1.From.Type = obj.TYPE_REG
    622 		p1.From.Reg = v.Args[1].Reg()
    623 		p1.Reg = v.Reg0()
    624 		p1.To.Type = obj.TYPE_REG
    625 		p1.To.Reg = mips.REGTMP
    626 
    627 		p2 := gc.Prog(mips.ASC)
    628 		p2.From.Type = obj.TYPE_REG
    629 		p2.From.Reg = mips.REGTMP
    630 		p2.To.Type = obj.TYPE_MEM
    631 		p2.To.Reg = v.Args[0].Reg()
    632 
    633 		p3 := gc.Prog(mips.ABEQ)
    634 		p3.From.Type = obj.TYPE_REG
    635 		p3.From.Reg = mips.REGTMP
    636 		p3.To.Type = obj.TYPE_BRANCH
    637 		gc.Patch(p3, p)
    638 
    639 		gc.Prog(mips.ASYNC)
    640 
    641 		p4 := gc.Prog(mips.AADDU)
    642 		p4.From.Type = obj.TYPE_REG
    643 		p4.From.Reg = v.Args[1].Reg()
    644 		p4.Reg = v.Reg0()
    645 		p4.To.Type = obj.TYPE_REG
    646 		p4.To.Reg = v.Reg0()
    647 
    648 	case ssa.OpMIPSLoweredAtomicAddconst:
    649 		// SYNC
    650 		// LL	(Rarg0), Rout
    651 		// ADDU $auxInt, Rout, Rtmp
    652 		// SC	Rtmp, (Rarg0)
    653 		// BEQ	Rtmp, -3(PC)
    654 		// SYNC
    655 		// ADDU $auxInt, Rout
    656 		gc.Prog(mips.ASYNC)
    657 
    658 		p := gc.Prog(mips.ALL)
    659 		p.From.Type = obj.TYPE_MEM
    660 		p.From.Reg = v.Args[0].Reg()
    661 		p.To.Type = obj.TYPE_REG
    662 		p.To.Reg = v.Reg0()
    663 
    664 		p1 := gc.Prog(mips.AADDU)
    665 		p1.From.Type = obj.TYPE_CONST
    666 		p1.From.Offset = v.AuxInt
    667 		p1.Reg = v.Reg0()
    668 		p1.To.Type = obj.TYPE_REG
    669 		p1.To.Reg = mips.REGTMP
    670 
    671 		p2 := gc.Prog(mips.ASC)
    672 		p2.From.Type = obj.TYPE_REG
    673 		p2.From.Reg = mips.REGTMP
    674 		p2.To.Type = obj.TYPE_MEM
    675 		p2.To.Reg = v.Args[0].Reg()
    676 
    677 		p3 := gc.Prog(mips.ABEQ)
    678 		p3.From.Type = obj.TYPE_REG
    679 		p3.From.Reg = mips.REGTMP
    680 		p3.To.Type = obj.TYPE_BRANCH
    681 		gc.Patch(p3, p)
    682 
    683 		gc.Prog(mips.ASYNC)
    684 
    685 		p4 := gc.Prog(mips.AADDU)
    686 		p4.From.Type = obj.TYPE_CONST
    687 		p4.From.Offset = v.AuxInt
    688 		p4.Reg = v.Reg0()
    689 		p4.To.Type = obj.TYPE_REG
    690 		p4.To.Reg = v.Reg0()
    691 
    692 	case ssa.OpMIPSLoweredAtomicAnd,
    693 		ssa.OpMIPSLoweredAtomicOr:
    694 		// SYNC
    695 		// LL	(Rarg0), Rtmp
    696 		// AND/OR	Rarg1, Rtmp
    697 		// SC	Rtmp, (Rarg0)
    698 		// BEQ	Rtmp, -3(PC)
    699 		// SYNC
    700 		gc.Prog(mips.ASYNC)
    701 
    702 		p := gc.Prog(mips.ALL)
    703 		p.From.Type = obj.TYPE_MEM
    704 		p.From.Reg = v.Args[0].Reg()
    705 		p.To.Type = obj.TYPE_REG
    706 		p.To.Reg = mips.REGTMP
    707 
    708 		p1 := gc.Prog(v.Op.Asm())
    709 		p1.From.Type = obj.TYPE_REG
    710 		p1.From.Reg = v.Args[1].Reg()
    711 		p1.Reg = mips.REGTMP
    712 		p1.To.Type = obj.TYPE_REG
    713 		p1.To.Reg = mips.REGTMP
    714 
    715 		p2 := gc.Prog(mips.ASC)
    716 		p2.From.Type = obj.TYPE_REG
    717 		p2.From.Reg = mips.REGTMP
    718 		p2.To.Type = obj.TYPE_MEM
    719 		p2.To.Reg = v.Args[0].Reg()
    720 
    721 		p3 := gc.Prog(mips.ABEQ)
    722 		p3.From.Type = obj.TYPE_REG
    723 		p3.From.Reg = mips.REGTMP
    724 		p3.To.Type = obj.TYPE_BRANCH
    725 		gc.Patch(p3, p)
    726 
    727 		gc.Prog(mips.ASYNC)
    728 
    729 	case ssa.OpMIPSLoweredAtomicCas:
    730 		// MOVW $0, Rout
    731 		// SYNC
    732 		// LL	(Rarg0), Rtmp
    733 		// BNE	Rtmp, Rarg1, 4(PC)
    734 		// MOVW Rarg2, Rout
    735 		// SC	Rout, (Rarg0)
    736 		// BEQ	Rout, -4(PC)
    737 		// SYNC
    738 		p := gc.Prog(mips.AMOVW)
    739 		p.From.Type = obj.TYPE_REG
    740 		p.From.Reg = mips.REGZERO
    741 		p.To.Type = obj.TYPE_REG
    742 		p.To.Reg = v.Reg0()
    743 
    744 		gc.Prog(mips.ASYNC)
    745 
    746 		p1 := gc.Prog(mips.ALL)
    747 		p1.From.Type = obj.TYPE_MEM
    748 		p1.From.Reg = v.Args[0].Reg()
    749 		p1.To.Type = obj.TYPE_REG
    750 		p1.To.Reg = mips.REGTMP
    751 
    752 		p2 := gc.Prog(mips.ABNE)
    753 		p2.From.Type = obj.TYPE_REG
    754 		p2.From.Reg = v.Args[1].Reg()
    755 		p2.Reg = mips.REGTMP
    756 		p2.To.Type = obj.TYPE_BRANCH
    757 
    758 		p3 := gc.Prog(mips.AMOVW)
    759 		p3.From.Type = obj.TYPE_REG
    760 		p3.From.Reg = v.Args[2].Reg()
    761 		p3.To.Type = obj.TYPE_REG
    762 		p3.To.Reg = v.Reg0()
    763 
    764 		p4 := gc.Prog(mips.ASC)
    765 		p4.From.Type = obj.TYPE_REG
    766 		p4.From.Reg = v.Reg0()
    767 		p4.To.Type = obj.TYPE_MEM
    768 		p4.To.Reg = v.Args[0].Reg()
    769 
    770 		p5 := gc.Prog(mips.ABEQ)
    771 		p5.From.Type = obj.TYPE_REG
    772 		p5.From.Reg = v.Reg0()
    773 		p5.To.Type = obj.TYPE_BRANCH
    774 		gc.Patch(p5, p1)
    775 
    776 		gc.Prog(mips.ASYNC)
    777 
    778 		p6 := gc.Prog(obj.ANOP)
    779 		gc.Patch(p2, p6)
    780 
    781 	case ssa.OpVarDef:
    782 		gc.Gvardef(v.Aux.(*gc.Node))
    783 	case ssa.OpVarKill:
    784 		gc.Gvarkill(v.Aux.(*gc.Node))
    785 	case ssa.OpVarLive:
    786 		gc.Gvarlive(v.Aux.(*gc.Node))
    787 	case ssa.OpKeepAlive:
    788 		gc.KeepAlive(v)
    789 	case ssa.OpPhi:
    790 		gc.CheckLoweredPhi(v)
    791 	case ssa.OpMIPSLoweredNilCheck:
    792 		// Issue a load which will fault if arg is nil.
    793 		p := gc.Prog(mips.AMOVB)
    794 		p.From.Type = obj.TYPE_MEM
    795 		p.From.Reg = v.Args[0].Reg()
    796 		gc.AddAux(&p.From, v)
    797 		p.To.Type = obj.TYPE_REG
    798 		p.To.Reg = mips.REGTMP
    799 		if gc.Debug_checknil != 0 && v.Line > 1 { // v.Line==1 in generated wrappers
    800 			gc.Warnl(v.Line, "generated nil check")
    801 		}
    802 	case ssa.OpMIPSFPFlagTrue,
    803 		ssa.OpMIPSFPFlagFalse:
    804 		// MOVW		$1, r
    805 		// CMOVF	R0, r
    806 
    807 		cmov := mips.ACMOVF
    808 		if v.Op == ssa.OpMIPSFPFlagFalse {
    809 			cmov = mips.ACMOVT
    810 		}
    811 		p := gc.Prog(mips.AMOVW)
    812 		p.From.Type = obj.TYPE_CONST
    813 		p.From.Offset = 1
    814 		p.To.Type = obj.TYPE_REG
    815 		p.To.Reg = v.Reg()
    816 		p1 := gc.Prog(cmov)
    817 		p1.From.Type = obj.TYPE_REG
    818 		p1.From.Reg = mips.REGZERO
    819 		p1.To.Type = obj.TYPE_REG
    820 		p1.To.Reg = v.Reg()
    821 
    822 	case ssa.OpMIPSLoweredGetClosurePtr:
    823 		// Closure pointer is R22 (mips.REGCTXT).
    824 		gc.CheckLoweredGetClosurePtr(v)
    825 	default:
    826 		v.Fatalf("genValue not implemented: %s", v.LongString())
    827 	}
    828 }
    829 
    830 var blockJump = map[ssa.BlockKind]struct {
    831 	asm, invasm obj.As
    832 }{
    833 	ssa.BlockMIPSEQ:  {mips.ABEQ, mips.ABNE},
    834 	ssa.BlockMIPSNE:  {mips.ABNE, mips.ABEQ},
    835 	ssa.BlockMIPSLTZ: {mips.ABLTZ, mips.ABGEZ},
    836 	ssa.BlockMIPSGEZ: {mips.ABGEZ, mips.ABLTZ},
    837 	ssa.BlockMIPSLEZ: {mips.ABLEZ, mips.ABGTZ},
    838 	ssa.BlockMIPSGTZ: {mips.ABGTZ, mips.ABLEZ},
    839 	ssa.BlockMIPSFPT: {mips.ABFPT, mips.ABFPF},
    840 	ssa.BlockMIPSFPF: {mips.ABFPF, mips.ABFPT},
    841 }
    842 
    843 func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
    844 	s.SetLineno(b.Line)
    845 
    846 	switch b.Kind {
    847 	case ssa.BlockPlain:
    848 		if b.Succs[0].Block() != next {
    849 			p := gc.Prog(obj.AJMP)
    850 			p.To.Type = obj.TYPE_BRANCH
    851 			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
    852 		}
    853 	case ssa.BlockDefer:
    854 		// defer returns in R1:
    855 		// 0 if we should continue executing
    856 		// 1 if we should jump to deferreturn call
    857 		p := gc.Prog(mips.ABNE)
    858 		p.From.Type = obj.TYPE_REG
    859 		p.From.Reg = mips.REGZERO
    860 		p.Reg = mips.REG_R1
    861 		p.To.Type = obj.TYPE_BRANCH
    862 		s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
    863 		if b.Succs[0].Block() != next {
    864 			p := gc.Prog(obj.AJMP)
    865 			p.To.Type = obj.TYPE_BRANCH
    866 			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
    867 		}
    868 	case ssa.BlockExit:
    869 		gc.Prog(obj.AUNDEF) // tell plive.go that we never reach here
    870 	case ssa.BlockRet:
    871 		gc.Prog(obj.ARET)
    872 	case ssa.BlockRetJmp:
    873 		p := gc.Prog(obj.ARET)
    874 		p.To.Type = obj.TYPE_MEM
    875 		p.To.Name = obj.NAME_EXTERN
    876 		p.To.Sym = gc.Linksym(b.Aux.(*gc.Sym))
    877 	case ssa.BlockMIPSEQ, ssa.BlockMIPSNE,
    878 		ssa.BlockMIPSLTZ, ssa.BlockMIPSGEZ,
    879 		ssa.BlockMIPSLEZ, ssa.BlockMIPSGTZ,
    880 		ssa.BlockMIPSFPT, ssa.BlockMIPSFPF:
    881 		jmp := blockJump[b.Kind]
    882 		var p *obj.Prog
    883 		switch next {
    884 		case b.Succs[0].Block():
    885 			p = gc.Prog(jmp.invasm)
    886 			p.To.Type = obj.TYPE_BRANCH
    887 			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
    888 		case b.Succs[1].Block():
    889 			p = gc.Prog(jmp.asm)
    890 			p.To.Type = obj.TYPE_BRANCH
    891 			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
    892 		default:
    893 			p = gc.Prog(jmp.asm)
    894 			p.To.Type = obj.TYPE_BRANCH
    895 			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
    896 			q := gc.Prog(obj.AJMP)
    897 			q.To.Type = obj.TYPE_BRANCH
    898 			s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()})
    899 		}
    900 		if !b.Control.Type.IsFlags() {
    901 			p.From.Type = obj.TYPE_REG
    902 			p.From.Reg = b.Control.Reg()
    903 		}
    904 	default:
    905 		b.Fatalf("branch not implemented: %s. Control: %s", b.LongString(), b.Control.LongString())
    906 	}
    907 }
    908