// 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 mips64 import ( "math" "cmd/compile/internal/gc" "cmd/compile/internal/ssa" "cmd/compile/internal/types" "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 *types.Type, r int16) obj.As { if isFPreg(r) { if t.Size() == 4 { // float32 or int32 return mips.AMOVF } else { // float64 or int64 return mips.AMOVD } } else { switch t.Size() { case 1: if t.IsSigned() { return mips.AMOVB } else { return mips.AMOVBU } case 2: if t.IsSigned() { return mips.AMOVH } else { return mips.AMOVHU } case 4: if t.IsSigned() { return mips.AMOVW } else { return mips.AMOVWU } case 8: return mips.AMOVV } } panic("bad load type") } // storeByType returns the store instruction of the given type. func storeByType(t *types.Type, r int16) obj.As { if isFPreg(r) { if t.Size() == 4 { // float32 or int32 return mips.AMOVF } else { // float64 or int64 return mips.AMOVD } } else { switch t.Size() { case 1: return mips.AMOVB case 2: return mips.AMOVH case 4: return mips.AMOVW case 8: return mips.AMOVV } } panic("bad store type") } func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { switch v.Op { case ssa.OpCopy, ssa.OpMIPS64MOVVconvert, ssa.OpMIPS64MOVVreg: if v.Type.IsMemory() { return } x := v.Args[0].Reg() y := v.Reg() if x == y { return } as := mips.AMOVV if isFPreg(x) && isFPreg(y) { as = mips.AMOVD } p := s.Prog(as) p.From.Type = obj.TYPE_REG p.From.Reg = x p.To.Type = obj.TYPE_REG p.To.Reg = y 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 = s.Prog(mips.AMOVV) p.From.Type = obj.TYPE_REG p.From.Reg = mips.REGTMP p.To.Type = obj.TYPE_REG p.To.Reg = y } case ssa.OpMIPS64MOVVnop: 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 := s.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 = s.Prog(mips.AMOVV) 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 := s.Prog(mips.AMOVV) 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 := s.Prog(storeByType(v.Type, r)) p.From.Type = obj.TYPE_REG p.From.Reg = r gc.AddrAuto(&p.To, v) case ssa.OpMIPS64ADDV, ssa.OpMIPS64SUBV, ssa.OpMIPS64AND, ssa.OpMIPS64OR, ssa.OpMIPS64XOR, ssa.OpMIPS64NOR, ssa.OpMIPS64SLLV, ssa.OpMIPS64SRLV, ssa.OpMIPS64SRAV, ssa.OpMIPS64ADDF, ssa.OpMIPS64ADDD, ssa.OpMIPS64SUBF, ssa.OpMIPS64SUBD, ssa.OpMIPS64MULF, ssa.OpMIPS64MULD, ssa.OpMIPS64DIVF, ssa.OpMIPS64DIVD: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[1].Reg() p.Reg = v.Args[0].Reg() p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpMIPS64SGT, ssa.OpMIPS64SGTU: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[0].Reg() p.Reg = v.Args[1].Reg() p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpMIPS64ADDVconst, ssa.OpMIPS64SUBVconst, ssa.OpMIPS64ANDconst, ssa.OpMIPS64ORconst, ssa.OpMIPS64XORconst, ssa.OpMIPS64NORconst, ssa.OpMIPS64SLLVconst, ssa.OpMIPS64SRLVconst, ssa.OpMIPS64SRAVconst, ssa.OpMIPS64SGTconst, ssa.OpMIPS64SGTUconst: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_CONST p.From.Offset = v.AuxInt p.Reg = v.Args[0].Reg() p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpMIPS64MULV, ssa.OpMIPS64MULVU, ssa.OpMIPS64DIVV, ssa.OpMIPS64DIVVU: // result in hi,lo p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[1].Reg() p.Reg = v.Args[0].Reg() case ssa.OpMIPS64MOVVconst: r := v.Reg() p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_CONST p.From.Offset = v.AuxInt p.To.Type = obj.TYPE_REG p.To.Reg = r if isFPreg(r) || isHILO(r) { // cannot move into FP or special registers, use TMP as intermediate p.To.Reg = mips.REGTMP p = s.Prog(mips.AMOVV) p.From.Type = obj.TYPE_REG p.From.Reg = mips.REGTMP p.To.Type = obj.TYPE_REG p.To.Reg = r } case ssa.OpMIPS64MOVFconst, ssa.OpMIPS64MOVDconst: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_FCONST p.From.Val = math.Float64frombits(uint64(v.AuxInt)) p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpMIPS64CMPEQF, ssa.OpMIPS64CMPEQD, ssa.OpMIPS64CMPGEF, ssa.OpMIPS64CMPGED, ssa.OpMIPS64CMPGTF, ssa.OpMIPS64CMPGTD: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[0].Reg() p.Reg = v.Args[1].Reg() case ssa.OpMIPS64MOVVaddr: p := s.Prog(mips.AMOVV) p.From.Type = obj.TYPE_ADDR p.From.Reg = v.Args[0].Reg() var wantreg string // MOVV $sym+off(base), R // the assembler expands it as the following: // - base is SP: add constant offset to SP (R29) // when constant is large, tmp register (R23) may be used // - base is SB: load external address with relocation switch v.Aux.(type) { default: v.Fatalf("aux is of unknown type %T", v.Aux) case *obj.LSym: wantreg = "SB" gc.AddAux(&p.From, v) case *gc.Node: wantreg = "SP" gc.AddAux(&p.From, v) case nil: // No sym, just MOVV $off(SP), R wantreg = "SP" p.From.Offset = v.AuxInt } if reg := v.Args[0].RegName(); reg != wantreg { v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg) } p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpMIPS64MOVBload, ssa.OpMIPS64MOVBUload, ssa.OpMIPS64MOVHload, ssa.OpMIPS64MOVHUload, ssa.OpMIPS64MOVWload, ssa.OpMIPS64MOVWUload, ssa.OpMIPS64MOVVload, ssa.OpMIPS64MOVFload, ssa.OpMIPS64MOVDload: p := s.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.OpMIPS64MOVBstore, ssa.OpMIPS64MOVHstore, ssa.OpMIPS64MOVWstore, ssa.OpMIPS64MOVVstore, ssa.OpMIPS64MOVFstore, ssa.OpMIPS64MOVDstore: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[1].Reg() p.To.Type = obj.TYPE_MEM p.To.Reg = v.Args[0].Reg() gc.AddAux(&p.To, v) case ssa.OpMIPS64MOVBstorezero, ssa.OpMIPS64MOVHstorezero, ssa.OpMIPS64MOVWstorezero, ssa.OpMIPS64MOVVstorezero: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG p.From.Reg = mips.REGZERO p.To.Type = obj.TYPE_MEM p.To.Reg = v.Args[0].Reg() gc.AddAux(&p.To, v) case ssa.OpMIPS64MOVBreg, ssa.OpMIPS64MOVBUreg, ssa.OpMIPS64MOVHreg, ssa.OpMIPS64MOVHUreg, ssa.OpMIPS64MOVWreg, ssa.OpMIPS64MOVWUreg: a := v.Args[0] for a.Op == ssa.OpCopy || a.Op == ssa.OpMIPS64MOVVreg { a = a.Args[0] } if a.Op == ssa.OpLoadReg { t := a.Type switch { case v.Op == ssa.OpMIPS64MOVBreg && t.Size() == 1 && t.IsSigned(), v.Op == ssa.OpMIPS64MOVBUreg && t.Size() == 1 && !t.IsSigned(), v.Op == ssa.OpMIPS64MOVHreg && t.Size() == 2 && t.IsSigned(), v.Op == ssa.OpMIPS64MOVHUreg && t.Size() == 2 && !t.IsSigned(), v.Op == ssa.OpMIPS64MOVWreg && t.Size() == 4 && t.IsSigned(), v.Op == ssa.OpMIPS64MOVWUreg && t.Size() == 4 && !t.IsSigned(): // arg is a proper-typed load, already zero/sign-extended, don't extend again if v.Reg() == v.Args[0].Reg() { return } p := s.Prog(mips.AMOVV) 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.OpMIPS64MOVWF, ssa.OpMIPS64MOVWD, ssa.OpMIPS64TRUNCFW, ssa.OpMIPS64TRUNCDW, ssa.OpMIPS64MOVVF, ssa.OpMIPS64MOVVD, ssa.OpMIPS64TRUNCFV, ssa.OpMIPS64TRUNCDV, ssa.OpMIPS64MOVFD, ssa.OpMIPS64MOVDF, ssa.OpMIPS64NEGF, ssa.OpMIPS64NEGD: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[0].Reg() p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpMIPS64NEGV: // SUB from REGZERO p := s.Prog(mips.ASUBVU) 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.OpMIPS64DUFFZERO: // runtime.duffzero expects start address - 8 in R1 p := s.Prog(mips.ASUBVU) p.From.Type = obj.TYPE_CONST p.From.Offset = 8 p.Reg = v.Args[0].Reg() p.To.Type = obj.TYPE_REG p.To.Reg = mips.REG_R1 p = s.Prog(obj.ADUFFZERO) p.To.Type = obj.TYPE_MEM p.To.Name = obj.NAME_EXTERN p.To.Sym = gc.Duffzero p.To.Offset = v.AuxInt case ssa.OpMIPS64LoweredZero: // SUBV $8, R1 // MOVV R0, 8(R1) // ADDV $8, 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%8 == 0: sz = 8 mov = mips.AMOVV case v.AuxInt%4 == 0: sz = 4 mov = mips.AMOVW case v.AuxInt%2 == 0: sz = 2 mov = mips.AMOVH default: sz = 1 mov = mips.AMOVB } p := s.Prog(mips.ASUBVU) p.From.Type = obj.TYPE_CONST p.From.Offset = sz p.To.Type = obj.TYPE_REG p.To.Reg = mips.REG_R1 p2 := s.Prog(mov) p2.From.Type = obj.TYPE_REG p2.From.Reg = mips.REGZERO p2.To.Type = obj.TYPE_MEM p2.To.Reg = mips.REG_R1 p2.To.Offset = sz p3 := s.Prog(mips.AADDVU) p3.From.Type = obj.TYPE_CONST p3.From.Offset = sz p3.To.Type = obj.TYPE_REG p3.To.Reg = mips.REG_R1 p4 := s.Prog(mips.ABNE) p4.From.Type = obj.TYPE_REG p4.From.Reg = v.Args[1].Reg() p4.Reg = mips.REG_R1 p4.To.Type = obj.TYPE_BRANCH gc.Patch(p4, p2) case ssa.OpMIPS64LoweredMove: // SUBV $8, R1 // MOVV 8(R1), Rtmp // MOVV Rtmp, (R2) // ADDV $8, R1 // ADDV $8, 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%8 == 0: sz = 8 mov = mips.AMOVV case v.AuxInt%4 == 0: sz = 4 mov = mips.AMOVW case v.AuxInt%2 == 0: sz = 2 mov = mips.AMOVH default: sz = 1 mov = mips.AMOVB } p := s.Prog(mips.ASUBVU) p.From.Type = obj.TYPE_CONST p.From.Offset = sz p.To.Type = obj.TYPE_REG p.To.Reg = mips.REG_R1 p2 := s.Prog(mov) p2.From.Type = obj.TYPE_MEM p2.From.Reg = mips.REG_R1 p2.From.Offset = sz p2.To.Type = obj.TYPE_REG p2.To.Reg = mips.REGTMP p3 := s.Prog(mov) p3.From.Type = obj.TYPE_REG p3.From.Reg = mips.REGTMP p3.To.Type = obj.TYPE_MEM p3.To.Reg = mips.REG_R2 p4 := s.Prog(mips.AADDVU) p4.From.Type = obj.TYPE_CONST p4.From.Offset = sz p4.To.Type = obj.TYPE_REG p4.To.Reg = mips.REG_R1 p5 := s.Prog(mips.AADDVU) p5.From.Type = obj.TYPE_CONST p5.From.Offset = sz p5.To.Type = obj.TYPE_REG p5.To.Reg = mips.REG_R2 p6 := s.Prog(mips.ABNE) p6.From.Type = obj.TYPE_REG p6.From.Reg = v.Args[2].Reg() p6.Reg = mips.REG_R1 p6.To.Type = obj.TYPE_BRANCH gc.Patch(p6, p2) case ssa.OpMIPS64CALLstatic, ssa.OpMIPS64CALLclosure, ssa.OpMIPS64CALLinter: s.Call(v) case ssa.OpMIPS64LoweredAtomicLoad32, ssa.OpMIPS64LoweredAtomicLoad64: as := mips.AMOVV if v.Op == ssa.OpMIPS64LoweredAtomicLoad32 { as = mips.AMOVW } s.Prog(mips.ASYNC) p := s.Prog(as) p.From.Type = obj.TYPE_MEM p.From.Reg = v.Args[0].Reg() p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg0() s.Prog(mips.ASYNC) case ssa.OpMIPS64LoweredAtomicStore32, ssa.OpMIPS64LoweredAtomicStore64: as := mips.AMOVV if v.Op == ssa.OpMIPS64LoweredAtomicStore32 { as = mips.AMOVW } s.Prog(mips.ASYNC) p := s.Prog(as) p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[1].Reg() p.To.Type = obj.TYPE_MEM p.To.Reg = v.Args[0].Reg() s.Prog(mips.ASYNC) case ssa.OpMIPS64LoweredAtomicStorezero32, ssa.OpMIPS64LoweredAtomicStorezero64: as := mips.AMOVV if v.Op == ssa.OpMIPS64LoweredAtomicStorezero32 { as = mips.AMOVW } s.Prog(mips.ASYNC) p := s.Prog(as) p.From.Type = obj.TYPE_REG p.From.Reg = mips.REGZERO p.To.Type = obj.TYPE_MEM p.To.Reg = v.Args[0].Reg() s.Prog(mips.ASYNC) case ssa.OpMIPS64LoweredAtomicExchange32, ssa.OpMIPS64LoweredAtomicExchange64: // SYNC // MOVV Rarg1, Rtmp // LL (Rarg0), Rout // SC Rtmp, (Rarg0) // BEQ Rtmp, -3(PC) // SYNC ll := mips.ALLV sc := mips.ASCV if v.Op == ssa.OpMIPS64LoweredAtomicExchange32 { ll = mips.ALL sc = mips.ASC } s.Prog(mips.ASYNC) p := s.Prog(mips.AMOVV) p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[1].Reg() p.To.Type = obj.TYPE_REG p.To.Reg = mips.REGTMP p1 := s.Prog(ll) p1.From.Type = obj.TYPE_MEM p1.From.Reg = v.Args[0].Reg() p1.To.Type = obj.TYPE_REG p1.To.Reg = v.Reg0() p2 := s.Prog(sc) p2.From.Type = obj.TYPE_REG p2.From.Reg = mips.REGTMP p2.To.Type = obj.TYPE_MEM p2.To.Reg = v.Args[0].Reg() p3 := s.Prog(mips.ABEQ) p3.From.Type = obj.TYPE_REG p3.From.Reg = mips.REGTMP p3.To.Type = obj.TYPE_BRANCH gc.Patch(p3, p) s.Prog(mips.ASYNC) case ssa.OpMIPS64LoweredAtomicAdd32, ssa.OpMIPS64LoweredAtomicAdd64: // SYNC // LL (Rarg0), Rout // ADDV Rarg1, Rout, Rtmp // SC Rtmp, (Rarg0) // BEQ Rtmp, -3(PC) // SYNC // ADDV Rarg1, Rout ll := mips.ALLV sc := mips.ASCV if v.Op == ssa.OpMIPS64LoweredAtomicAdd32 { ll = mips.ALL sc = mips.ASC } s.Prog(mips.ASYNC) p := s.Prog(ll) p.From.Type = obj.TYPE_MEM p.From.Reg = v.Args[0].Reg() p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg0() p1 := s.Prog(mips.AADDVU) p1.From.Type = obj.TYPE_REG p1.From.Reg = v.Args[1].Reg() p1.Reg = v.Reg0() p1.To.Type = obj.TYPE_REG p1.To.Reg = mips.REGTMP p2 := s.Prog(sc) p2.From.Type = obj.TYPE_REG p2.From.Reg = mips.REGTMP p2.To.Type = obj.TYPE_MEM p2.To.Reg = v.Args[0].Reg() p3 := s.Prog(mips.ABEQ) p3.From.Type = obj.TYPE_REG p3.From.Reg = mips.REGTMP p3.To.Type = obj.TYPE_BRANCH gc.Patch(p3, p) s.Prog(mips.ASYNC) p4 := s.Prog(mips.AADDVU) 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.OpMIPS64LoweredAtomicAddconst32, ssa.OpMIPS64LoweredAtomicAddconst64: // SYNC // LL (Rarg0), Rout // ADDV $auxint, Rout, Rtmp // SC Rtmp, (Rarg0) // BEQ Rtmp, -3(PC) // SYNC // ADDV $auxint, Rout ll := mips.ALLV sc := mips.ASCV if v.Op == ssa.OpMIPS64LoweredAtomicAddconst32 { ll = mips.ALL sc = mips.ASC } s.Prog(mips.ASYNC) p := s.Prog(ll) p.From.Type = obj.TYPE_MEM p.From.Reg = v.Args[0].Reg() p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg0() p1 := s.Prog(mips.AADDVU) p1.From.Type = obj.TYPE_CONST p1.From.Offset = v.AuxInt p1.Reg = v.Reg0() p1.To.Type = obj.TYPE_REG p1.To.Reg = mips.REGTMP p2 := s.Prog(sc) p2.From.Type = obj.TYPE_REG p2.From.Reg = mips.REGTMP p2.To.Type = obj.TYPE_MEM p2.To.Reg = v.Args[0].Reg() p3 := s.Prog(mips.ABEQ) p3.From.Type = obj.TYPE_REG p3.From.Reg = mips.REGTMP p3.To.Type = obj.TYPE_BRANCH gc.Patch(p3, p) s.Prog(mips.ASYNC) p4 := s.Prog(mips.AADDVU) 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.OpMIPS64LoweredAtomicCas32, ssa.OpMIPS64LoweredAtomicCas64: // MOVV $0, Rout // SYNC // LL (Rarg0), Rtmp // BNE Rtmp, Rarg1, 4(PC) // MOVV Rarg2, Rout // SC Rout, (Rarg0) // BEQ Rout, -4(PC) // SYNC ll := mips.ALLV sc := mips.ASCV if v.Op == ssa.OpMIPS64LoweredAtomicCas32 { ll = mips.ALL sc = mips.ASC } p := s.Prog(mips.AMOVV) p.From.Type = obj.TYPE_REG p.From.Reg = mips.REGZERO p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg0() s.Prog(mips.ASYNC) p1 := s.Prog(ll) p1.From.Type = obj.TYPE_MEM p1.From.Reg = v.Args[0].Reg() p1.To.Type = obj.TYPE_REG p1.To.Reg = mips.REGTMP p2 := s.Prog(mips.ABNE) p2.From.Type = obj.TYPE_REG p2.From.Reg = v.Args[1].Reg() p2.Reg = mips.REGTMP p2.To.Type = obj.TYPE_BRANCH p3 := s.Prog(mips.AMOVV) p3.From.Type = obj.TYPE_REG p3.From.Reg = v.Args[2].Reg() p3.To.Type = obj.TYPE_REG p3.To.Reg = v.Reg0() p4 := s.Prog(sc) p4.From.Type = obj.TYPE_REG p4.From.Reg = v.Reg0() p4.To.Type = obj.TYPE_MEM p4.To.Reg = v.Args[0].Reg() p5 := s.Prog(mips.ABEQ) p5.From.Type = obj.TYPE_REG p5.From.Reg = v.Reg0() p5.To.Type = obj.TYPE_BRANCH gc.Patch(p5, p1) p6 := s.Prog(mips.ASYNC) gc.Patch(p2, p6) case ssa.OpMIPS64LoweredNilCheck: // Issue a load which will fault if arg is nil. p := s.Prog(mips.AMOVB) p.From.Type = obj.TYPE_MEM p.From.Reg = v.Args[0].Reg() gc.AddAux(&p.From, v) p.To.Type = obj.TYPE_REG p.To.Reg = mips.REGTMP if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers gc.Warnl(v.Pos, "generated nil check") } case ssa.OpMIPS64FPFlagTrue, ssa.OpMIPS64FPFlagFalse: // MOVV $0, r // BFPF 2(PC) // MOVV $1, r branch := mips.ABFPF if v.Op == ssa.OpMIPS64FPFlagFalse { branch = mips.ABFPT } p := s.Prog(mips.AMOVV) p.From.Type = obj.TYPE_REG p.From.Reg = mips.REGZERO p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() p2 := s.Prog(branch) p2.To.Type = obj.TYPE_BRANCH p3 := s.Prog(mips.AMOVV) p3.From.Type = obj.TYPE_CONST p3.From.Offset = 1 p3.To.Type = obj.TYPE_REG p3.To.Reg = v.Reg() p4 := s.Prog(obj.ANOP) // not a machine instruction, for branch to land gc.Patch(p2, p4) case ssa.OpMIPS64LoweredGetClosurePtr: // Closure pointer is R22 (mips.REGCTXT). gc.CheckLoweredGetClosurePtr(v) case ssa.OpMIPS64LoweredGetCallerSP: // caller's SP is FixedFrameSize below the address of the first arg p := s.Prog(mips.AMOVV) p.From.Type = obj.TYPE_ADDR p.From.Offset = -gc.Ctxt.FixedFrameSize() p.From.Name = obj.NAME_PARAM p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpClobber: // TODO: implement for clobberdead experiment. Nop is ok for now. default: v.Fatalf("genValue not implemented: %s", v.LongString()) } } var blockJump = map[ssa.BlockKind]struct { asm, invasm obj.As }{ ssa.BlockMIPS64EQ: {mips.ABEQ, mips.ABNE}, ssa.BlockMIPS64NE: {mips.ABNE, mips.ABEQ}, ssa.BlockMIPS64LTZ: {mips.ABLTZ, mips.ABGEZ}, ssa.BlockMIPS64GEZ: {mips.ABGEZ, mips.ABLTZ}, ssa.BlockMIPS64LEZ: {mips.ABLEZ, mips.ABGTZ}, ssa.BlockMIPS64GTZ: {mips.ABGTZ, mips.ABLEZ}, ssa.BlockMIPS64FPT: {mips.ABFPT, mips.ABFPF}, ssa.BlockMIPS64FPF: {mips.ABFPF, mips.ABFPT}, } func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) { switch b.Kind { case ssa.BlockPlain: if b.Succs[0].Block() != next { p := s.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 := s.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 := s.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: s.Prog(obj.AUNDEF) // tell plive.go that we never reach here case ssa.BlockRet: s.Prog(obj.ARET) case ssa.BlockRetJmp: p := s.Prog(obj.ARET) p.To.Type = obj.TYPE_MEM p.To.Name = obj.NAME_EXTERN p.To.Sym = b.Aux.(*obj.LSym) case ssa.BlockMIPS64EQ, ssa.BlockMIPS64NE, ssa.BlockMIPS64LTZ, ssa.BlockMIPS64GEZ, ssa.BlockMIPS64LEZ, ssa.BlockMIPS64GTZ, ssa.BlockMIPS64FPT, ssa.BlockMIPS64FPF: jmp := blockJump[b.Kind] var p *obj.Prog switch next { case b.Succs[0].Block(): p = s.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 = s.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 = s.Prog(jmp.asm) p.To.Type = obj.TYPE_BRANCH s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()}) q := s.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()) } }