// 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 riscv64 import ( "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/logopt" "cmd/compile/internal/objw" "cmd/compile/internal/ssa" "cmd/compile/internal/ssagen" "cmd/compile/internal/types" "cmd/internal/obj" "cmd/internal/obj/riscv" ) // ssaRegToReg maps ssa register numbers to obj register numbers. var ssaRegToReg = []int16{ riscv.REG_X0, // X1 (LR): unused riscv.REG_X2, riscv.REG_X3, riscv.REG_X4, riscv.REG_X5, riscv.REG_X6, riscv.REG_X7, riscv.REG_X8, riscv.REG_X9, riscv.REG_X10, riscv.REG_X11, riscv.REG_X12, riscv.REG_X13, riscv.REG_X14, riscv.REG_X15, riscv.REG_X16, riscv.REG_X17, riscv.REG_X18, riscv.REG_X19, riscv.REG_X20, riscv.REG_X21, riscv.REG_X22, riscv.REG_X23, riscv.REG_X24, riscv.REG_X25, riscv.REG_X26, riscv.REG_X27, riscv.REG_X28, riscv.REG_X29, riscv.REG_X30, riscv.REG_X31, riscv.REG_F0, riscv.REG_F1, riscv.REG_F2, riscv.REG_F3, riscv.REG_F4, riscv.REG_F5, riscv.REG_F6, riscv.REG_F7, riscv.REG_F8, riscv.REG_F9, riscv.REG_F10, riscv.REG_F11, riscv.REG_F12, riscv.REG_F13, riscv.REG_F14, riscv.REG_F15, riscv.REG_F16, riscv.REG_F17, riscv.REG_F18, riscv.REG_F19, riscv.REG_F20, riscv.REG_F21, riscv.REG_F22, riscv.REG_F23, riscv.REG_F24, riscv.REG_F25, riscv.REG_F26, riscv.REG_F27, riscv.REG_F28, riscv.REG_F29, riscv.REG_F30, riscv.REG_F31, 0, // SB isn't a real register. We fill an Addr.Reg field with 0 in this case. } func loadByType(t *types.Type) obj.As { width := t.Size() if t.IsFloat() { switch width { case 4: return riscv.AMOVF case 8: return riscv.AMOVD default: base.Fatalf("unknown float width for load %d in type %v", width, t) return 0 } } switch width { case 1: if t.IsSigned() { return riscv.AMOVB } else { return riscv.AMOVBU } case 2: if t.IsSigned() { return riscv.AMOVH } else { return riscv.AMOVHU } case 4: if t.IsSigned() { return riscv.AMOVW } else { return riscv.AMOVWU } case 8: return riscv.AMOV default: base.Fatalf("unknown width for load %d in type %v", width, t) return 0 } } // storeByType returns the store instruction of the given type. func storeByType(t *types.Type) obj.As { width := t.Size() if t.IsFloat() { switch width { case 4: return riscv.AMOVF case 8: return riscv.AMOVD default: base.Fatalf("unknown float width for store %d in type %v", width, t) return 0 } } switch width { case 1: return riscv.AMOVB case 2: return riscv.AMOVH case 4: return riscv.AMOVW case 8: return riscv.AMOV default: base.Fatalf("unknown width for store %d in type %v", width, t) return 0 } } // largestMove returns the largest move instruction possible and its size, // given the alignment of the total size of the move. // // e.g., a 16-byte move may use MOV, but an 11-byte move must use MOVB. // // Note that the moves may not be on naturally aligned addresses depending on // the source and destination. // // This matches the calculation in ssa.moveSize. func largestMove(alignment int64) (obj.As, int64) { switch { case alignment%8 == 0: return riscv.AMOV, 8 case alignment%4 == 0: return riscv.AMOVW, 4 case alignment%2 == 0: return riscv.AMOVH, 2 default: return riscv.AMOVB, 1 } } // ssaMarkMoves marks any MOVXconst ops that need to avoid clobbering flags. // RISC-V has no flags, so this is a no-op. func ssaMarkMoves(s *ssagen.State, b *ssa.Block) {} func ssaGenValue(s *ssagen.State, v *ssa.Value) { s.SetPos(v.Pos) switch v.Op { case ssa.OpInitMem: // memory arg needs no code case ssa.OpArg: // input args need no code case ssa.OpPhi: ssagen.CheckLoweredPhi(v) case ssa.OpCopy, ssa.OpRISCV64MOVDreg: if v.Type.IsMemory() { return } rs := v.Args[0].Reg() rd := v.Reg() if rs == rd { return } as := riscv.AMOV if v.Type.IsFloat() { as = riscv.AMOVD } p := s.Prog(as) p.From.Type = obj.TYPE_REG p.From.Reg = rs p.To.Type = obj.TYPE_REG p.To.Reg = rd case ssa.OpRISCV64MOVDnop: // nothing to do case ssa.OpLoadReg: if v.Type.IsFlags() { v.Fatalf("load flags not implemented: %v", v.LongString()) return } p := s.Prog(loadByType(v.Type)) ssagen.AddrAuto(&p.From, v.Args[0]) p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpStoreReg: if v.Type.IsFlags() { v.Fatalf("store flags not implemented: %v", v.LongString()) return } p := s.Prog(storeByType(v.Type)) p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[0].Reg() ssagen.AddrAuto(&p.To, v) case ssa.OpArgIntReg, ssa.OpArgFloatReg: // The assembler needs to wrap the entry safepoint/stack growth code with spill/unspill // The loop only runs once. for _, a := range v.Block.Func.RegArgs { // Pass the spill/unspill information along to the assembler, offset by size of // the saved LR slot. addr := ssagen.SpillSlotAddr(a, riscv.REG_SP, base.Ctxt.Arch.FixedFrameSize) s.FuncInfo().AddSpill( obj.RegSpill{Reg: a.Reg, Addr: addr, Unspill: loadByType(a.Type), Spill: storeByType(a.Type)}) } v.Block.Func.RegArgs = nil ssagen.CheckArgReg(v) case ssa.OpSP, ssa.OpSB, ssa.OpGetG: // nothing to do case ssa.OpRISCV64MOVBreg, ssa.OpRISCV64MOVHreg, ssa.OpRISCV64MOVWreg, ssa.OpRISCV64MOVBUreg, ssa.OpRISCV64MOVHUreg, ssa.OpRISCV64MOVWUreg: a := v.Args[0] for a.Op == ssa.OpCopy || a.Op == ssa.OpRISCV64MOVDreg { a = a.Args[0] } as := v.Op.Asm() rs := v.Args[0].Reg() rd := v.Reg() if a.Op == ssa.OpLoadReg { t := a.Type switch { case v.Op == ssa.OpRISCV64MOVBreg && t.Size() == 1 && t.IsSigned(), v.Op == ssa.OpRISCV64MOVHreg && t.Size() == 2 && t.IsSigned(), v.Op == ssa.OpRISCV64MOVWreg && t.Size() == 4 && t.IsSigned(), v.Op == ssa.OpRISCV64MOVBUreg && t.Size() == 1 && !t.IsSigned(), v.Op == ssa.OpRISCV64MOVHUreg && t.Size() == 2 && !t.IsSigned(), v.Op == ssa.OpRISCV64MOVWUreg && t.Size() == 4 && !t.IsSigned(): // arg is a proper-typed load and already sign/zero-extended if rs == rd { return } as = riscv.AMOV default: } } p := s.Prog(as) p.From.Type = obj.TYPE_REG p.From.Reg = rs p.To.Type = obj.TYPE_REG p.To.Reg = rd case ssa.OpRISCV64ADD, ssa.OpRISCV64SUB, ssa.OpRISCV64SUBW, ssa.OpRISCV64XNOR, ssa.OpRISCV64XOR, ssa.OpRISCV64OR, ssa.OpRISCV64ORN, ssa.OpRISCV64AND, ssa.OpRISCV64ANDN, ssa.OpRISCV64SLL, ssa.OpRISCV64SLLW, ssa.OpRISCV64SRA, ssa.OpRISCV64SRAW, ssa.OpRISCV64SRL, ssa.OpRISCV64SRLW, ssa.OpRISCV64SLT, ssa.OpRISCV64SLTU, ssa.OpRISCV64MUL, ssa.OpRISCV64MULW, ssa.OpRISCV64MULH, ssa.OpRISCV64MULHU, ssa.OpRISCV64DIV, ssa.OpRISCV64DIVU, ssa.OpRISCV64DIVW, ssa.OpRISCV64DIVUW, ssa.OpRISCV64REM, ssa.OpRISCV64REMU, ssa.OpRISCV64REMW, ssa.OpRISCV64REMUW, ssa.OpRISCV64ROL, ssa.OpRISCV64ROLW, ssa.OpRISCV64ROR, ssa.OpRISCV64RORW, ssa.OpRISCV64FADDS, ssa.OpRISCV64FSUBS, ssa.OpRISCV64FMULS, ssa.OpRISCV64FDIVS, ssa.OpRISCV64FEQS, ssa.OpRISCV64FNES, ssa.OpRISCV64FLTS, ssa.OpRISCV64FLES, ssa.OpRISCV64FADDD, ssa.OpRISCV64FSUBD, ssa.OpRISCV64FMULD, ssa.OpRISCV64FDIVD, ssa.OpRISCV64FEQD, ssa.OpRISCV64FNED, ssa.OpRISCV64FLTD, ssa.OpRISCV64FLED, ssa.OpRISCV64FSGNJD, ssa.OpRISCV64MIN, ssa.OpRISCV64MAX, ssa.OpRISCV64MINU, ssa.OpRISCV64MAXU, ssa.OpRISCV64SH1ADD, ssa.OpRISCV64SH2ADD, ssa.OpRISCV64SH3ADD: r := v.Reg() r1 := v.Args[0].Reg() r2 := v.Args[1].Reg() p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG p.From.Reg = r2 p.Reg = r1 p.To.Type = obj.TYPE_REG p.To.Reg = r case ssa.OpRISCV64LoweredFMAXD, ssa.OpRISCV64LoweredFMIND, ssa.OpRISCV64LoweredFMAXS, ssa.OpRISCV64LoweredFMINS: // Most of FMIN/FMAX result match Go's required behaviour, unless one of the // inputs is a NaN. As such, we need to explicitly test for NaN // before using FMIN/FMAX. // FADD Rarg0, Rarg1, Rout // FADD is used to propagate a NaN to the result in these cases. // FEQ Rarg0, Rarg0, Rtmp // BEQZ Rtmp, end // FEQ Rarg1, Rarg1, Rtmp // BEQZ Rtmp, end // F(MIN | MAX) r0 := v.Args[0].Reg() r1 := v.Args[1].Reg() out := v.Reg() add, feq := riscv.AFADDD, riscv.AFEQD if v.Op == ssa.OpRISCV64LoweredFMAXS || v.Op == ssa.OpRISCV64LoweredFMINS { add = riscv.AFADDS feq = riscv.AFEQS } p1 := s.Prog(add) p1.From.Type = obj.TYPE_REG p1.From.Reg = r0 p1.Reg = r1 p1.To.Type = obj.TYPE_REG p1.To.Reg = out p2 := s.Prog(feq) p2.From.Type = obj.TYPE_REG p2.From.Reg = r0 p2.Reg = r0 p2.To.Type = obj.TYPE_REG p2.To.Reg = riscv.REG_TMP p3 := s.Prog(riscv.ABEQ) p3.From.Type = obj.TYPE_REG p3.From.Reg = riscv.REG_ZERO p3.Reg = riscv.REG_TMP p3.To.Type = obj.TYPE_BRANCH p4 := s.Prog(feq) p4.From.Type = obj.TYPE_REG p4.From.Reg = r1 p4.Reg = r1 p4.To.Type = obj.TYPE_REG p4.To.Reg = riscv.REG_TMP p5 := s.Prog(riscv.ABEQ) p5.From.Type = obj.TYPE_REG p5.From.Reg = riscv.REG_ZERO p5.Reg = riscv.REG_TMP p5.To.Type = obj.TYPE_BRANCH p6 := s.Prog(v.Op.Asm()) p6.From.Type = obj.TYPE_REG p6.From.Reg = r1 p6.Reg = r0 p6.To.Type = obj.TYPE_REG p6.To.Reg = out nop := s.Prog(obj.ANOP) p3.To.SetTarget(nop) p5.To.SetTarget(nop) case ssa.OpRISCV64LoweredMuluhilo: r0 := v.Args[0].Reg() r1 := v.Args[1].Reg() p := s.Prog(riscv.AMULHU) p.From.Type = obj.TYPE_REG p.From.Reg = r1 p.Reg = r0 p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg0() p1 := s.Prog(riscv.AMUL) p1.From.Type = obj.TYPE_REG p1.From.Reg = r1 p1.Reg = r0 p1.To.Type = obj.TYPE_REG p1.To.Reg = v.Reg1() case ssa.OpRISCV64LoweredMuluover: r0 := v.Args[0].Reg() r1 := v.Args[1].Reg() p := s.Prog(riscv.AMULHU) p.From.Type = obj.TYPE_REG p.From.Reg = r1 p.Reg = r0 p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg1() p1 := s.Prog(riscv.AMUL) p1.From.Type = obj.TYPE_REG p1.From.Reg = r1 p1.Reg = r0 p1.To.Type = obj.TYPE_REG p1.To.Reg = v.Reg0() p2 := s.Prog(riscv.ASNEZ) p2.From.Type = obj.TYPE_REG p2.From.Reg = v.Reg1() p2.To.Type = obj.TYPE_REG p2.To.Reg = v.Reg1() case ssa.OpRISCV64FMADDD, ssa.OpRISCV64FMSUBD, ssa.OpRISCV64FNMADDD, ssa.OpRISCV64FNMSUBD, ssa.OpRISCV64FMADDS, ssa.OpRISCV64FMSUBS, ssa.OpRISCV64FNMADDS, ssa.OpRISCV64FNMSUBS: r := v.Reg() r1 := v.Args[0].Reg() r2 := v.Args[1].Reg() r3 := v.Args[2].Reg() p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG p.From.Reg = r2 p.Reg = r1 p.AddRestSource(obj.Addr{Type: obj.TYPE_REG, Reg: r3}) p.To.Type = obj.TYPE_REG p.To.Reg = r case ssa.OpRISCV64FSQRTS, ssa.OpRISCV64FNEGS, ssa.OpRISCV64FABSD, ssa.OpRISCV64FSQRTD, ssa.OpRISCV64FNEGD, ssa.OpRISCV64FMVSX, ssa.OpRISCV64FMVDX, ssa.OpRISCV64FCVTSW, ssa.OpRISCV64FCVTSL, ssa.OpRISCV64FCVTWS, ssa.OpRISCV64FCVTLS, ssa.OpRISCV64FCVTDW, ssa.OpRISCV64FCVTDL, ssa.OpRISCV64FCVTWD, ssa.OpRISCV64FCVTLD, ssa.OpRISCV64FCVTDS, ssa.OpRISCV64FCVTSD, ssa.OpRISCV64NOT, ssa.OpRISCV64NEG, ssa.OpRISCV64NEGW: 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.OpRISCV64ADDI, ssa.OpRISCV64ADDIW, ssa.OpRISCV64XORI, ssa.OpRISCV64ORI, ssa.OpRISCV64ANDI, ssa.OpRISCV64SLLI, ssa.OpRISCV64SLLIW, ssa.OpRISCV64SRAI, ssa.OpRISCV64SRAIW, ssa.OpRISCV64SRLI, ssa.OpRISCV64SRLIW, ssa.OpRISCV64SLTI, ssa.OpRISCV64SLTIU, ssa.OpRISCV64RORI, ssa.OpRISCV64RORIW: 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.OpRISCV64MOVDconst: 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 = v.Reg() case ssa.OpRISCV64MOVaddr: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_ADDR p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() var wantreg string // MOVW $sym+off(base), R 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.Reg = riscv.REG_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) } case ssa.OpRISCV64MOVBload, ssa.OpRISCV64MOVHload, ssa.OpRISCV64MOVWload, ssa.OpRISCV64MOVDload, ssa.OpRISCV64MOVBUload, ssa.OpRISCV64MOVHUload, ssa.OpRISCV64MOVWUload, ssa.OpRISCV64FMOVWload, ssa.OpRISCV64FMOVDload: 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 ssa.OpRISCV64MOVBstore, ssa.OpRISCV64MOVHstore, ssa.OpRISCV64MOVWstore, ssa.OpRISCV64MOVDstore, ssa.OpRISCV64FMOVWstore, ssa.OpRISCV64FMOVDstore: 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 ssa.OpRISCV64MOVBstorezero, ssa.OpRISCV64MOVHstorezero, ssa.OpRISCV64MOVWstorezero, ssa.OpRISCV64MOVDstorezero: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REG p.From.Reg = riscv.REG_ZERO p.To.Type = obj.TYPE_MEM p.To.Reg = v.Args[0].Reg() ssagen.AddAux(&p.To, v) case ssa.OpRISCV64SEQZ, ssa.OpRISCV64SNEZ: 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.OpRISCV64CALLstatic, ssa.OpRISCV64CALLclosure, ssa.OpRISCV64CALLinter: s.Call(v) case ssa.OpRISCV64CALLtail: s.TailCall(v) case ssa.OpRISCV64LoweredWB: 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 ssa.OpRISCV64LoweredPanicBoundsA, ssa.OpRISCV64LoweredPanicBoundsB, ssa.OpRISCV64LoweredPanicBoundsC: p := s.Prog(obj.ACALL) p.To.Type = obj.TYPE_MEM p.To.Name = obj.NAME_EXTERN p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt] s.UseArgs(16) // space used in callee args area by assembly stubs case ssa.OpRISCV64LoweredAtomicLoad8: s.Prog(riscv.AFENCE) p := s.Prog(riscv.AMOVBU) 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(riscv.AFENCE) case ssa.OpRISCV64LoweredAtomicLoad32, ssa.OpRISCV64LoweredAtomicLoad64: as := riscv.ALRW if v.Op == ssa.OpRISCV64LoweredAtomicLoad64 { as = riscv.ALRD } 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() case ssa.OpRISCV64LoweredAtomicStore8: s.Prog(riscv.AFENCE) p := s.Prog(riscv.AMOVB) 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(riscv.AFENCE) case ssa.OpRISCV64LoweredAtomicStore32, ssa.OpRISCV64LoweredAtomicStore64: as := riscv.AAMOSWAPW if v.Op == ssa.OpRISCV64LoweredAtomicStore64 { as = riscv.AAMOSWAPD } 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() p.RegTo2 = riscv.REG_ZERO case ssa.OpRISCV64LoweredAtomicAdd32, ssa.OpRISCV64LoweredAtomicAdd64: as := riscv.AAMOADDW if v.Op == ssa.OpRISCV64LoweredAtomicAdd64 { as = riscv.AAMOADDD } 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() p.RegTo2 = riscv.REG_TMP p2 := s.Prog(riscv.AADD) p2.From.Type = obj.TYPE_REG p2.From.Reg = riscv.REG_TMP p2.Reg = v.Args[1].Reg() p2.To.Type = obj.TYPE_REG p2.To.Reg = v.Reg0() case ssa.OpRISCV64LoweredAtomicExchange32, ssa.OpRISCV64LoweredAtomicExchange64: as := riscv.AAMOSWAPW if v.Op == ssa.OpRISCV64LoweredAtomicExchange64 { as = riscv.AAMOSWAPD } 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() p.RegTo2 = v.Reg0() case ssa.OpRISCV64LoweredAtomicCas32, ssa.OpRISCV64LoweredAtomicCas64: // MOV ZERO, Rout // LR (Rarg0), Rtmp // BNE Rtmp, Rarg1, 3(PC) // SC Rarg2, (Rarg0), Rtmp // BNE Rtmp, ZERO, -3(PC) // MOV $1, Rout lr := riscv.ALRW sc := riscv.ASCW if v.Op == ssa.OpRISCV64LoweredAtomicCas64 { lr = riscv.ALRD sc = riscv.ASCD } r0 := v.Args[0].Reg() r1 := v.Args[1].Reg() r2 := v.Args[2].Reg() out := v.Reg0() p := s.Prog(riscv.AMOV) p.From.Type = obj.TYPE_REG p.From.Reg = riscv.REG_ZERO p.To.Type = obj.TYPE_REG p.To.Reg = out p1 := s.Prog(lr) p1.From.Type = obj.TYPE_MEM p1.From.Reg = r0 p1.To.Type = obj.TYPE_REG p1.To.Reg = riscv.REG_TMP p2 := s.Prog(riscv.ABNE) p2.From.Type = obj.TYPE_REG p2.From.Reg = r1 p2.Reg = riscv.REG_TMP p2.To.Type = obj.TYPE_BRANCH p3 := s.Prog(sc) p3.From.Type = obj.TYPE_REG p3.From.Reg = r2 p3.To.Type = obj.TYPE_MEM p3.To.Reg = r0 p3.RegTo2 = riscv.REG_TMP p4 := s.Prog(riscv.ABNE) p4.From.Type = obj.TYPE_REG p4.From.Reg = riscv.REG_TMP p4.Reg = riscv.REG_ZERO p4.To.Type = obj.TYPE_BRANCH p4.To.SetTarget(p1) p5 := s.Prog(riscv.AMOV) p5.From.Type = obj.TYPE_CONST p5.From.Offset = 1 p5.To.Type = obj.TYPE_REG p5.To.Reg = out p6 := s.Prog(obj.ANOP) p2.To.SetTarget(p6) case ssa.OpRISCV64LoweredAtomicAnd32, ssa.OpRISCV64LoweredAtomicOr32: 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() p.RegTo2 = riscv.REG_ZERO case ssa.OpRISCV64LoweredZero: mov, sz := largestMove(v.AuxInt) // mov ZERO, (Rarg0) // ADD $sz, Rarg0 // BGEU Rarg1, Rarg0, -2(PC) p := s.Prog(mov) p.From.Type = obj.TYPE_REG p.From.Reg = riscv.REG_ZERO p.To.Type = obj.TYPE_MEM p.To.Reg = v.Args[0].Reg() p2 := s.Prog(riscv.AADD) p2.From.Type = obj.TYPE_CONST p2.From.Offset = sz p2.To.Type = obj.TYPE_REG p2.To.Reg = v.Args[0].Reg() p3 := s.Prog(riscv.ABGEU) p3.To.Type = obj.TYPE_BRANCH p3.Reg = v.Args[0].Reg() p3.From.Type = obj.TYPE_REG p3.From.Reg = v.Args[1].Reg() p3.To.SetTarget(p) case ssa.OpRISCV64LoweredMove: mov, sz := largestMove(v.AuxInt) // mov (Rarg1), T2 // mov T2, (Rarg0) // ADD $sz, Rarg0 // ADD $sz, Rarg1 // BGEU Rarg2, Rarg0, -4(PC) p := s.Prog(mov) p.From.Type = obj.TYPE_MEM p.From.Reg = v.Args[1].Reg() p.To.Type = obj.TYPE_REG p.To.Reg = riscv.REG_T2 p2 := s.Prog(mov) p2.From.Type = obj.TYPE_REG p2.From.Reg = riscv.REG_T2 p2.To.Type = obj.TYPE_MEM p2.To.Reg = v.Args[0].Reg() p3 := s.Prog(riscv.AADD) p3.From.Type = obj.TYPE_CONST p3.From.Offset = sz p3.To.Type = obj.TYPE_REG p3.To.Reg = v.Args[0].Reg() p4 := s.Prog(riscv.AADD) p4.From.Type = obj.TYPE_CONST p4.From.Offset = sz p4.To.Type = obj.TYPE_REG p4.To.Reg = v.Args[1].Reg() p5 := s.Prog(riscv.ABGEU) p5.To.Type = obj.TYPE_BRANCH p5.Reg = v.Args[1].Reg() p5.From.Type = obj.TYPE_REG p5.From.Reg = v.Args[2].Reg() p5.To.SetTarget(p) case ssa.OpRISCV64LoweredNilCheck: // Issue a load which will fault if arg is nil. p := s.Prog(riscv.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 = riscv.REG_ZERO if logopt.Enabled() { logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name) } if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos == 1 in generated wrappers base.WarnfAt(v.Pos, "generated nil check") } case ssa.OpRISCV64LoweredGetClosurePtr: // Closure pointer is S10 (riscv.REG_CTXT). ssagen.CheckLoweredGetClosurePtr(v) case ssa.OpRISCV64LoweredGetCallerSP: // caller's SP is FixedFrameSize below the address of the first arg p := s.Prog(riscv.AMOV) 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 ssa.OpRISCV64LoweredGetCallerPC: p := s.Prog(obj.AGETCALLERPC) p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() case ssa.OpRISCV64DUFFZERO: p := s.Prog(obj.ADUFFZERO) p.To.Type = obj.TYPE_MEM p.To.Name = obj.NAME_EXTERN p.To.Sym = ir.Syms.Duffzero p.To.Offset = v.AuxInt case ssa.OpRISCV64DUFFCOPY: p := s.Prog(obj.ADUFFCOPY) p.To.Type = obj.TYPE_MEM p.To.Name = obj.NAME_EXTERN p.To.Sym = ir.Syms.Duffcopy p.To.Offset = v.AuxInt case ssa.OpRISCV64LoweredPubBarrier: // FENCE s.Prog(v.Op.Asm()) case ssa.OpRISCV64LoweredRound32F, ssa.OpRISCV64LoweredRound64F: // input is already rounded case ssa.OpClobber, ssa.OpClobberReg: // TODO: implement for clobberdead experiment. Nop is ok for now. default: v.Fatalf("Unhandled op %v", v.Op) } } var blockBranch = [...]obj.As{ ssa.BlockRISCV64BEQ: riscv.ABEQ, ssa.BlockRISCV64BEQZ: riscv.ABEQZ, ssa.BlockRISCV64BGE: riscv.ABGE, ssa.BlockRISCV64BGEU: riscv.ABGEU, ssa.BlockRISCV64BGEZ: riscv.ABGEZ, ssa.BlockRISCV64BGTZ: riscv.ABGTZ, ssa.BlockRISCV64BLEZ: riscv.ABLEZ, ssa.BlockRISCV64BLT: riscv.ABLT, ssa.BlockRISCV64BLTU: riscv.ABLTU, ssa.BlockRISCV64BLTZ: riscv.ABLTZ, ssa.BlockRISCV64BNE: riscv.ABNE, ssa.BlockRISCV64BNEZ: riscv.ABNEZ, } func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { s.SetPos(b.Pos) switch b.Kind { case ssa.BlockDefer: // defer returns in A0: // 0 if we should continue executing // 1 if we should jump to deferreturn call p := s.Prog(riscv.ABNE) p.To.Type = obj.TYPE_BRANCH p.From.Type = obj.TYPE_REG p.From.Reg = riscv.REG_ZERO p.Reg = riscv.REG_A0 s.Branches = append(s.Branches, ssagen.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, ssagen.Branch{P: p, B: b.Succs[0].Block()}) } 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, ssagen.Branch{P: p, B: b.Succs[0].Block()}) } case ssa.BlockExit, ssa.BlockRetJmp: case ssa.BlockRet: s.Prog(obj.ARET) case ssa.BlockRISCV64BEQ, ssa.BlockRISCV64BEQZ, ssa.BlockRISCV64BNE, ssa.BlockRISCV64BNEZ, ssa.BlockRISCV64BLT, ssa.BlockRISCV64BLEZ, ssa.BlockRISCV64BGE, ssa.BlockRISCV64BGEZ, ssa.BlockRISCV64BLTZ, ssa.BlockRISCV64BGTZ, ssa.BlockRISCV64BLTU, ssa.BlockRISCV64BGEU: as := blockBranch[b.Kind] invAs := riscv.InvertBranch(as) var p *obj.Prog switch next { case b.Succs[0].Block(): p = s.Br(invAs, b.Succs[1].Block()) case b.Succs[1].Block(): p = s.Br(as, b.Succs[0].Block()) default: if b.Likely != ssa.BranchUnlikely { p = s.Br(as, b.Succs[0].Block()) s.Br(obj.AJMP, b.Succs[1].Block()) } else { p = s.Br(invAs, b.Succs[1].Block()) s.Br(obj.AJMP, b.Succs[0].Block()) } } p.From.Type = obj.TYPE_REG switch b.Kind { case ssa.BlockRISCV64BEQ, ssa.BlockRISCV64BNE, ssa.BlockRISCV64BLT, ssa.BlockRISCV64BGE, ssa.BlockRISCV64BLTU, ssa.BlockRISCV64BGEU: if b.NumControls() != 2 { b.Fatalf("Unexpected number of controls (%d != 2): %s", b.NumControls(), b.LongString()) } p.From.Reg = b.Controls[0].Reg() p.Reg = b.Controls[1].Reg() case ssa.BlockRISCV64BEQZ, ssa.BlockRISCV64BNEZ, ssa.BlockRISCV64BGEZ, ssa.BlockRISCV64BLEZ, ssa.BlockRISCV64BLTZ, ssa.BlockRISCV64BGTZ: if b.NumControls() != 1 { b.Fatalf("Unexpected number of controls (%d != 1): %s", b.NumControls(), b.LongString()) } p.From.Reg = b.Controls[0].Reg() } default: b.Fatalf("Unhandled block: %s", b.LongString()) } } func loadRegResult(s *ssagen.State, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog { p := s.Prog(loadByType(t)) p.From.Type = obj.TYPE_MEM p.From.Name = obj.NAME_AUTO p.From.Sym = n.Linksym() p.From.Offset = n.FrameOffset() + off p.To.Type = obj.TYPE_REG p.To.Reg = reg return p } func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog { p = pp.Append(p, storeByType(t), obj.TYPE_REG, reg, 0, obj.TYPE_MEM, 0, n.FrameOffset()+off) p.To.Name = obj.NAME_PARAM p.To.Sym = n.Linksym() p.Pos = p.Pos.WithNotStmt() return p }