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