Source file src/cmd/compile/internal/riscv64/ssa.go

     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 riscv64
     6  
     7  import (
     8  	"cmd/compile/internal/base"
     9  	"cmd/compile/internal/ir"
    10  	"cmd/compile/internal/logopt"
    11  	"cmd/compile/internal/objw"
    12  	"cmd/compile/internal/ssa"
    13  	"cmd/compile/internal/ssa/block"
    14  	"cmd/compile/internal/ssa/ssaop"
    15  	"cmd/compile/internal/ssagen"
    16  	"cmd/compile/internal/types"
    17  	"cmd/internal/obj"
    18  	"cmd/internal/obj/riscv"
    19  	"internal/abi"
    20  	"internal/buildcfg"
    21  )
    22  
    23  // ssaRegToReg maps ssa register numbers to obj register numbers.
    24  var ssaRegToReg = []int16{
    25  	riscv.REG_X0,
    26  	// X1 (LR): unused
    27  	riscv.REG_X2,
    28  	riscv.REG_X3,
    29  	riscv.REG_X4,
    30  	riscv.REG_X5,
    31  	riscv.REG_X6,
    32  	riscv.REG_X7,
    33  	riscv.REG_X8,
    34  	riscv.REG_X9,
    35  	riscv.REG_X10,
    36  	riscv.REG_X11,
    37  	riscv.REG_X12,
    38  	riscv.REG_X13,
    39  	riscv.REG_X14,
    40  	riscv.REG_X15,
    41  	riscv.REG_X16,
    42  	riscv.REG_X17,
    43  	riscv.REG_X18,
    44  	riscv.REG_X19,
    45  	riscv.REG_X20,
    46  	riscv.REG_X21,
    47  	riscv.REG_X22,
    48  	riscv.REG_X23,
    49  	riscv.REG_X24,
    50  	riscv.REG_X25,
    51  	riscv.REG_X26,
    52  	riscv.REG_X27,
    53  	riscv.REG_X28,
    54  	riscv.REG_X29,
    55  	riscv.REG_X30,
    56  	riscv.REG_X31,
    57  	riscv.REG_F0,
    58  	riscv.REG_F1,
    59  	riscv.REG_F2,
    60  	riscv.REG_F3,
    61  	riscv.REG_F4,
    62  	riscv.REG_F5,
    63  	riscv.REG_F6,
    64  	riscv.REG_F7,
    65  	riscv.REG_F8,
    66  	riscv.REG_F9,
    67  	riscv.REG_F10,
    68  	riscv.REG_F11,
    69  	riscv.REG_F12,
    70  	riscv.REG_F13,
    71  	riscv.REG_F14,
    72  	riscv.REG_F15,
    73  	riscv.REG_F16,
    74  	riscv.REG_F17,
    75  	riscv.REG_F18,
    76  	riscv.REG_F19,
    77  	riscv.REG_F20,
    78  	riscv.REG_F21,
    79  	riscv.REG_F22,
    80  	riscv.REG_F23,
    81  	riscv.REG_F24,
    82  	riscv.REG_F25,
    83  	riscv.REG_F26,
    84  	riscv.REG_F27,
    85  	riscv.REG_F28,
    86  	riscv.REG_F29,
    87  	riscv.REG_F30,
    88  	riscv.REG_F31,
    89  	0, // SB isn't a real register.  We fill an Addr.Reg field with 0 in this case.
    90  }
    91  
    92  func loadByType(t *types.Type) obj.As {
    93  	width := t.Size()
    94  
    95  	if t.IsFloat() {
    96  		switch width {
    97  		case 4:
    98  			return riscv.AMOVF
    99  		case 8:
   100  			return riscv.AMOVD
   101  		default:
   102  			base.Fatalf("unknown float width for load %d in type %v", width, t)
   103  			return 0
   104  		}
   105  	}
   106  
   107  	switch width {
   108  	case 1:
   109  		if t.IsSigned() {
   110  			return riscv.AMOVB
   111  		} else {
   112  			return riscv.AMOVBU
   113  		}
   114  	case 2:
   115  		if t.IsSigned() {
   116  			return riscv.AMOVH
   117  		} else {
   118  			return riscv.AMOVHU
   119  		}
   120  	case 4:
   121  		if t.IsSigned() {
   122  			return riscv.AMOVW
   123  		} else {
   124  			return riscv.AMOVWU
   125  		}
   126  	case 8:
   127  		return riscv.AMOV
   128  	default:
   129  		base.Fatalf("unknown width for load %d in type %v", width, t)
   130  		return 0
   131  	}
   132  }
   133  
   134  // storeByType returns the store instruction of the given type.
   135  func storeByType(t *types.Type) obj.As {
   136  	width := t.Size()
   137  
   138  	if t.IsFloat() {
   139  		switch width {
   140  		case 4:
   141  			return riscv.AMOVF
   142  		case 8:
   143  			return riscv.AMOVD
   144  		default:
   145  			base.Fatalf("unknown float width for store %d in type %v", width, t)
   146  			return 0
   147  		}
   148  	}
   149  
   150  	switch width {
   151  	case 1:
   152  		return riscv.AMOVB
   153  	case 2:
   154  		return riscv.AMOVH
   155  	case 4:
   156  		return riscv.AMOVW
   157  	case 8:
   158  		return riscv.AMOV
   159  	default:
   160  		base.Fatalf("unknown width for store %d in type %v", width, t)
   161  		return 0
   162  	}
   163  }
   164  
   165  // largestMove returns the largest move instruction possible and its size,
   166  // given the alignment of the total size of the move.
   167  //
   168  // e.g., a 16-byte move may use MOV, but an 11-byte move must use MOVB.
   169  //
   170  // Note that the moves may not be on naturally aligned addresses depending on
   171  // the source and destination.
   172  //
   173  // This matches the calculation in ssa.moveSize.
   174  func largestMove(alignment int64) (obj.As, int64) {
   175  	switch {
   176  	case alignment%8 == 0:
   177  		return riscv.AMOV, 8
   178  	case alignment%4 == 0:
   179  		return riscv.AMOVW, 4
   180  	case alignment%2 == 0:
   181  		return riscv.AMOVH, 2
   182  	default:
   183  		return riscv.AMOVB, 1
   184  	}
   185  }
   186  
   187  var fracMovOps = []obj.As{riscv.AMOVB, riscv.AMOVH, riscv.AMOVW, riscv.AMOV}
   188  
   189  // ssaMarkMoves marks any MOVXconst ops that need to avoid clobbering flags.
   190  // RISC-V has no flags, so this is a no-op.
   191  func ssaMarkMoves(s *ssagen.State, b *ssa.Block) {}
   192  
   193  func ssaGenValue(s *ssagen.State, v *ssa.Value) {
   194  	s.SetPos(v.Pos)
   195  
   196  	switch v.Op {
   197  	case ssaop.OpInitMem:
   198  		// memory arg needs no code
   199  	case ssaop.OpArg:
   200  		// input args need no code
   201  	case ssaop.OpPhi:
   202  		ssagen.CheckLoweredPhi(v)
   203  	case ssaop.OpCopy, ssaop.OpRISCV64MOVDreg:
   204  		if v.Type.IsMemory() {
   205  			return
   206  		}
   207  		rs := v.Args[0].Reg()
   208  		rd := v.Reg()
   209  		if rs == rd {
   210  			return
   211  		}
   212  		as := riscv.AMOV
   213  		if v.Type.IsFloat() {
   214  			as = riscv.AMOVD
   215  		}
   216  		p := s.Prog(as)
   217  		p.From.Type = obj.TYPE_REG
   218  		p.From.Reg = rs
   219  		p.To.Type = obj.TYPE_REG
   220  		p.To.Reg = rd
   221  	case ssaop.OpRISCV64MOVDnop:
   222  		// nothing to do
   223  	case ssaop.OpLoadReg:
   224  		if v.Type.IsFlags() {
   225  			v.Fatalf("load flags not implemented: %v", v.LongString())
   226  			return
   227  		}
   228  		p := s.Prog(loadByType(v.Type))
   229  		ssagen.AddrAuto(&p.From, v.Args[0])
   230  		p.To.Type = obj.TYPE_REG
   231  		p.To.Reg = v.Reg()
   232  	case ssaop.OpStoreReg:
   233  		if v.Type.IsFlags() {
   234  			v.Fatalf("store flags not implemented: %v", v.LongString())
   235  			return
   236  		}
   237  		p := s.Prog(storeByType(v.Type))
   238  		p.From.Type = obj.TYPE_REG
   239  		p.From.Reg = v.Args[0].Reg()
   240  		ssagen.AddrAuto(&p.To, v)
   241  	case ssaop.OpArgIntReg, ssaop.OpArgFloatReg:
   242  		// The assembler needs to wrap the entry safepoint/stack growth code with spill/unspill
   243  		// The loop only runs once.
   244  		for _, a := range v.Block.Func.RegArgs {
   245  			// Pass the spill/unspill information along to the assembler, offset by size of
   246  			// the saved LR slot.
   247  			addr := ssagen.SpillSlotAddr(a, riscv.REG_SP, base.Ctxt.Arch.FixedFrameSize)
   248  			s.FuncInfo().AddSpill(
   249  				obj.RegSpill{Reg: a.Reg, Addr: addr, Unspill: loadByType(a.Type), Spill: storeByType(a.Type)})
   250  		}
   251  		v.Block.Func.RegArgs = nil
   252  
   253  		ssagen.CheckArgReg(v)
   254  	case ssaop.OpSP, ssaop.OpSB, ssaop.OpGetG:
   255  		// nothing to do
   256  	case ssaop.OpRISCV64MOVBreg, ssaop.OpRISCV64MOVHreg, ssaop.OpRISCV64MOVWreg,
   257  		ssaop.OpRISCV64MOVBUreg, ssaop.OpRISCV64MOVHUreg, ssaop.OpRISCV64MOVWUreg:
   258  		a := v.Args[0]
   259  		for a.Op == ssaop.OpCopy || a.Op == ssaop.OpRISCV64MOVDreg {
   260  			a = a.Args[0]
   261  		}
   262  		as := v.Op.Asm()
   263  		rs := v.Args[0].Reg()
   264  		rd := v.Reg()
   265  		if a.Op == ssaop.OpLoadReg {
   266  			t := a.Type
   267  			switch {
   268  			case v.Op == ssaop.OpRISCV64MOVBreg && t.Size() == 1 && t.IsSigned(),
   269  				v.Op == ssaop.OpRISCV64MOVHreg && t.Size() == 2 && t.IsSigned(),
   270  				v.Op == ssaop.OpRISCV64MOVWreg && t.Size() == 4 && t.IsSigned(),
   271  				v.Op == ssaop.OpRISCV64MOVBUreg && t.Size() == 1 && !t.IsSigned(),
   272  				v.Op == ssaop.OpRISCV64MOVHUreg && t.Size() == 2 && !t.IsSigned(),
   273  				v.Op == ssaop.OpRISCV64MOVWUreg && t.Size() == 4 && !t.IsSigned():
   274  				// arg is a proper-typed load and already sign/zero-extended
   275  				if rs == rd {
   276  					return
   277  				}
   278  				as = riscv.AMOV
   279  			default:
   280  			}
   281  		}
   282  		p := s.Prog(as)
   283  		p.From.Type = obj.TYPE_REG
   284  		p.From.Reg = rs
   285  		p.To.Type = obj.TYPE_REG
   286  		p.To.Reg = rd
   287  	case ssaop.OpRISCV64ADD, ssaop.OpRISCV64SUB, ssaop.OpRISCV64SUBW, ssaop.OpRISCV64XNOR, ssaop.OpRISCV64XOR,
   288  		ssaop.OpRISCV64OR, ssaop.OpRISCV64ORN, ssaop.OpRISCV64AND, ssaop.OpRISCV64ANDN,
   289  		ssaop.OpRISCV64SLL, ssaop.OpRISCV64SLLW, ssaop.OpRISCV64SRA, ssaop.OpRISCV64SRAW, ssaop.OpRISCV64SRL, ssaop.OpRISCV64SRLW,
   290  		ssaop.OpRISCV64SLT, ssaop.OpRISCV64SLTU, ssaop.OpRISCV64MUL, ssaop.OpRISCV64MULW, ssaop.OpRISCV64MULH,
   291  		ssaop.OpRISCV64MULHU, ssaop.OpRISCV64DIV, ssaop.OpRISCV64DIVU, ssaop.OpRISCV64DIVW,
   292  		ssaop.OpRISCV64DIVUW, ssaop.OpRISCV64REM, ssaop.OpRISCV64REMU, ssaop.OpRISCV64REMW,
   293  		ssaop.OpRISCV64REMUW,
   294  		ssaop.OpRISCV64ROL, ssaop.OpRISCV64ROLW, ssaop.OpRISCV64ROR, ssaop.OpRISCV64RORW,
   295  		ssaop.OpRISCV64FADDS, ssaop.OpRISCV64FSUBS, ssaop.OpRISCV64FMULS, ssaop.OpRISCV64FDIVS,
   296  		ssaop.OpRISCV64FEQS, ssaop.OpRISCV64FNES, ssaop.OpRISCV64FLTS, ssaop.OpRISCV64FLES,
   297  		ssaop.OpRISCV64FADDD, ssaop.OpRISCV64FSUBD, ssaop.OpRISCV64FMULD, ssaop.OpRISCV64FDIVD,
   298  		ssaop.OpRISCV64FEQD, ssaop.OpRISCV64FNED, ssaop.OpRISCV64FLTD, ssaop.OpRISCV64FLED, ssaop.OpRISCV64FSGNJD,
   299  		ssaop.OpRISCV64MIN, ssaop.OpRISCV64MAX, ssaop.OpRISCV64MINU, ssaop.OpRISCV64MAXU,
   300  		ssaop.OpRISCV64SH1ADD, ssaop.OpRISCV64SH2ADD, ssaop.OpRISCV64SH3ADD,
   301  		ssaop.OpRISCV64CZEROEQZ, ssaop.OpRISCV64CZERONEZ:
   302  		r := v.Reg()
   303  		r1 := v.Args[0].Reg()
   304  		r2 := v.Args[1].Reg()
   305  		p := s.Prog(v.Op.Asm())
   306  		p.From.Type = obj.TYPE_REG
   307  		p.From.Reg = r2
   308  		p.Reg = r1
   309  		p.To.Type = obj.TYPE_REG
   310  		p.To.Reg = r
   311  
   312  	case ssaop.OpRISCV64LoweredFMAXD, ssaop.OpRISCV64LoweredFMIND, ssaop.OpRISCV64LoweredFMAXS, ssaop.OpRISCV64LoweredFMINS:
   313  		// Most of FMIN/FMAX result match Go's required behaviour, unless one of the
   314  		// inputs is a NaN. As such, we need to explicitly test for NaN
   315  		// before using FMIN/FMAX.
   316  
   317  		// FADD Rarg0, Rarg1, Rout // FADD is used to propagate a NaN to the result in these cases.
   318  		// FEQ  Rarg0, Rarg0, Rtmp
   319  		// BEQZ Rtmp, end
   320  		// FEQ  Rarg1, Rarg1, Rtmp
   321  		// BEQZ Rtmp, end
   322  		// F(MIN | MAX)
   323  
   324  		r0 := v.Args[0].Reg()
   325  		r1 := v.Args[1].Reg()
   326  		out := v.Reg()
   327  		add, feq := riscv.AFADDD, riscv.AFEQD
   328  		if v.Op == ssaop.OpRISCV64LoweredFMAXS || v.Op == ssaop.OpRISCV64LoweredFMINS {
   329  			add = riscv.AFADDS
   330  			feq = riscv.AFEQS
   331  		}
   332  
   333  		p1 := s.Prog(add)
   334  		p1.From.Type = obj.TYPE_REG
   335  		p1.From.Reg = r0
   336  		p1.Reg = r1
   337  		p1.To.Type = obj.TYPE_REG
   338  		p1.To.Reg = out
   339  
   340  		p2 := s.Prog(feq)
   341  		p2.From.Type = obj.TYPE_REG
   342  		p2.From.Reg = r0
   343  		p2.Reg = r0
   344  		p2.To.Type = obj.TYPE_REG
   345  		p2.To.Reg = riscv.REG_TMP
   346  
   347  		p3 := s.Prog(riscv.ABEQ)
   348  		p3.From.Type = obj.TYPE_REG
   349  		p3.From.Reg = riscv.REG_ZERO
   350  		p3.Reg = riscv.REG_TMP
   351  		p3.To.Type = obj.TYPE_BRANCH
   352  
   353  		p4 := s.Prog(feq)
   354  		p4.From.Type = obj.TYPE_REG
   355  		p4.From.Reg = r1
   356  		p4.Reg = r1
   357  		p4.To.Type = obj.TYPE_REG
   358  		p4.To.Reg = riscv.REG_TMP
   359  
   360  		p5 := s.Prog(riscv.ABEQ)
   361  		p5.From.Type = obj.TYPE_REG
   362  		p5.From.Reg = riscv.REG_ZERO
   363  		p5.Reg = riscv.REG_TMP
   364  		p5.To.Type = obj.TYPE_BRANCH
   365  
   366  		p6 := s.Prog(v.Op.Asm())
   367  		p6.From.Type = obj.TYPE_REG
   368  		p6.From.Reg = r1
   369  		p6.Reg = r0
   370  		p6.To.Type = obj.TYPE_REG
   371  		p6.To.Reg = out
   372  
   373  		nop := s.Prog(obj.ANOP)
   374  		p3.To.SetTarget(nop)
   375  		p5.To.SetTarget(nop)
   376  
   377  	case ssaop.OpRISCV64LoweredMuluhilo:
   378  		r0 := v.Args[0].Reg()
   379  		r1 := v.Args[1].Reg()
   380  		p := s.Prog(riscv.AMULHU)
   381  		p.From.Type = obj.TYPE_REG
   382  		p.From.Reg = r1
   383  		p.Reg = r0
   384  		p.To.Type = obj.TYPE_REG
   385  		p.To.Reg = v.Reg0()
   386  		p1 := s.Prog(riscv.AMUL)
   387  		p1.From.Type = obj.TYPE_REG
   388  		p1.From.Reg = r1
   389  		p1.Reg = r0
   390  		p1.To.Type = obj.TYPE_REG
   391  		p1.To.Reg = v.Reg1()
   392  	case ssaop.OpRISCV64LoweredMuluover:
   393  		r0 := v.Args[0].Reg()
   394  		r1 := v.Args[1].Reg()
   395  		p := s.Prog(riscv.AMULHU)
   396  		p.From.Type = obj.TYPE_REG
   397  		p.From.Reg = r1
   398  		p.Reg = r0
   399  		p.To.Type = obj.TYPE_REG
   400  		p.To.Reg = v.Reg1()
   401  		p1 := s.Prog(riscv.AMUL)
   402  		p1.From.Type = obj.TYPE_REG
   403  		p1.From.Reg = r1
   404  		p1.Reg = r0
   405  		p1.To.Type = obj.TYPE_REG
   406  		p1.To.Reg = v.Reg0()
   407  		p2 := s.Prog(riscv.ASNEZ)
   408  		p2.From.Type = obj.TYPE_REG
   409  		p2.From.Reg = v.Reg1()
   410  		p2.To.Type = obj.TYPE_REG
   411  		p2.To.Reg = v.Reg1()
   412  	case ssaop.OpRISCV64FMADDD, ssaop.OpRISCV64FMSUBD, ssaop.OpRISCV64FNMADDD, ssaop.OpRISCV64FNMSUBD,
   413  		ssaop.OpRISCV64FMADDS, ssaop.OpRISCV64FMSUBS, ssaop.OpRISCV64FNMADDS, ssaop.OpRISCV64FNMSUBS:
   414  		r := v.Reg()
   415  		r1 := v.Args[0].Reg()
   416  		r2 := v.Args[1].Reg()
   417  		r3 := v.Args[2].Reg()
   418  		p := s.Prog(v.Op.Asm())
   419  		p.From.Type = obj.TYPE_REG
   420  		p.From.Reg = r2
   421  		p.Reg = r1
   422  		p.AddRestSource(obj.Addr{Type: obj.TYPE_REG, Reg: r3})
   423  		p.To.Type = obj.TYPE_REG
   424  		p.To.Reg = r
   425  	case ssaop.OpRISCV64FSQRTS, ssaop.OpRISCV64FSQRTD,
   426  		ssaop.OpRISCV64FNEGS, ssaop.OpRISCV64FNEGD,
   427  		ssaop.OpRISCV64FABSS, ssaop.OpRISCV64FABSD,
   428  		ssaop.OpRISCV64FMVSX, ssaop.OpRISCV64FMVXS, ssaop.OpRISCV64FMVDX, ssaop.OpRISCV64FMVXD,
   429  		ssaop.OpRISCV64FCVTSW, ssaop.OpRISCV64FCVTSL, ssaop.OpRISCV64FCVTWS, ssaop.OpRISCV64FCVTLS,
   430  		ssaop.OpRISCV64FCVTDW, ssaop.OpRISCV64FCVTDL, ssaop.OpRISCV64FCVTWD, ssaop.OpRISCV64FCVTLD, ssaop.OpRISCV64FCVTDS, ssaop.OpRISCV64FCVTSD,
   431  		ssaop.OpRISCV64FCLASSS, ssaop.OpRISCV64FCLASSD,
   432  		ssaop.OpRISCV64NOT, ssaop.OpRISCV64NEG, ssaop.OpRISCV64NEGW, ssaop.OpRISCV64CLZ, ssaop.OpRISCV64CLZW, ssaop.OpRISCV64CTZ, ssaop.OpRISCV64CTZW,
   433  		ssaop.OpRISCV64REV8, ssaop.OpRISCV64CPOP, ssaop.OpRISCV64CPOPW:
   434  		p := s.Prog(v.Op.Asm())
   435  		p.From.Type = obj.TYPE_REG
   436  		p.From.Reg = v.Args[0].Reg()
   437  		p.To.Type = obj.TYPE_REG
   438  		p.To.Reg = v.Reg()
   439  	case ssaop.OpRISCV64ADDI, ssaop.OpRISCV64ADDIW, ssaop.OpRISCV64XORI, ssaop.OpRISCV64ORI, ssaop.OpRISCV64ANDI,
   440  		ssaop.OpRISCV64SLLI, ssaop.OpRISCV64SLLIW, ssaop.OpRISCV64SRAI, ssaop.OpRISCV64SRAIW,
   441  		ssaop.OpRISCV64SRLI, ssaop.OpRISCV64SRLIW, ssaop.OpRISCV64SLTI, ssaop.OpRISCV64SLTIU,
   442  		ssaop.OpRISCV64RORI, ssaop.OpRISCV64RORIW:
   443  		p := s.Prog(v.Op.Asm())
   444  		p.From.Type = obj.TYPE_CONST
   445  		p.From.Offset = v.AuxInt
   446  		p.Reg = v.Args[0].Reg()
   447  		p.To.Type = obj.TYPE_REG
   448  		p.To.Reg = v.Reg()
   449  	case ssaop.OpRISCV64MOVDconst:
   450  		p := s.Prog(v.Op.Asm())
   451  		p.From.Type = obj.TYPE_CONST
   452  		p.From.Offset = v.AuxInt
   453  		p.To.Type = obj.TYPE_REG
   454  		p.To.Reg = v.Reg()
   455  	case ssaop.OpRISCV64FMOVDconst, ssaop.OpRISCV64FMOVFconst:
   456  		p := s.Prog(v.Op.Asm())
   457  		p.From.Type = obj.TYPE_FCONST
   458  		p.From.Val = v.AuxFloat()
   459  		p.From.Name = obj.NAME_NONE
   460  		p.From.Reg = obj.REG_NONE
   461  		p.To.Type = obj.TYPE_REG
   462  		p.To.Reg = v.Reg()
   463  	case ssaop.OpRISCV64MOVaddr:
   464  		p := s.Prog(v.Op.Asm())
   465  		p.From.Type = obj.TYPE_ADDR
   466  		p.To.Type = obj.TYPE_REG
   467  		p.To.Reg = v.Reg()
   468  
   469  		var wantreg string
   470  		// MOVW $sym+off(base), R
   471  		switch v.Aux.(type) {
   472  		default:
   473  			v.Fatalf("aux is of unknown type %T", v.Aux)
   474  		case *obj.LSym:
   475  			wantreg = "SB"
   476  			ssagen.AddAux(&p.From, v)
   477  		case *ir.Name:
   478  			wantreg = "SP"
   479  			ssagen.AddAux(&p.From, v)
   480  		case nil:
   481  			// No sym, just MOVW $off(SP), R
   482  			wantreg = "SP"
   483  			p.From.Reg = riscv.REG_SP
   484  			p.From.Offset = v.AuxInt
   485  		}
   486  		if reg := v.Args[0].RegName(); reg != wantreg {
   487  			v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg)
   488  		}
   489  	case ssaop.OpRISCV64MOVBload, ssaop.OpRISCV64MOVHload, ssaop.OpRISCV64MOVWload, ssaop.OpRISCV64MOVDload,
   490  		ssaop.OpRISCV64MOVBUload, ssaop.OpRISCV64MOVHUload, ssaop.OpRISCV64MOVWUload,
   491  		ssaop.OpRISCV64FMOVWload, ssaop.OpRISCV64FMOVDload:
   492  		p := s.Prog(v.Op.Asm())
   493  		p.From.Type = obj.TYPE_MEM
   494  		p.From.Reg = v.Args[0].Reg()
   495  		ssagen.AddAux(&p.From, v)
   496  		p.To.Type = obj.TYPE_REG
   497  		p.To.Reg = v.Reg()
   498  	case ssaop.OpRISCV64MOVBstore, ssaop.OpRISCV64MOVHstore, ssaop.OpRISCV64MOVWstore, ssaop.OpRISCV64MOVDstore,
   499  		ssaop.OpRISCV64FMOVWstore, ssaop.OpRISCV64FMOVDstore:
   500  		p := s.Prog(v.Op.Asm())
   501  		p.From.Type = obj.TYPE_REG
   502  		p.From.Reg = v.Args[1].Reg()
   503  		p.To.Type = obj.TYPE_MEM
   504  		p.To.Reg = v.Args[0].Reg()
   505  		ssagen.AddAux(&p.To, v)
   506  	case ssaop.OpRISCV64MOVBstorezero, ssaop.OpRISCV64MOVHstorezero, ssaop.OpRISCV64MOVWstorezero, ssaop.OpRISCV64MOVDstorezero:
   507  		p := s.Prog(v.Op.Asm())
   508  		p.From.Type = obj.TYPE_REG
   509  		p.From.Reg = riscv.REG_ZERO
   510  		p.To.Type = obj.TYPE_MEM
   511  		p.To.Reg = v.Args[0].Reg()
   512  		ssagen.AddAux(&p.To, v)
   513  	case ssaop.OpRISCV64SEQZ, ssaop.OpRISCV64SNEZ:
   514  		p := s.Prog(v.Op.Asm())
   515  		p.From.Type = obj.TYPE_REG
   516  		p.From.Reg = v.Args[0].Reg()
   517  		p.To.Type = obj.TYPE_REG
   518  		p.To.Reg = v.Reg()
   519  	case ssaop.OpRISCV64CALLstatic, ssaop.OpRISCV64CALLclosure, ssaop.OpRISCV64CALLinter:
   520  		s.Call(v)
   521  	case ssaop.OpRISCV64CALLtail, ssaop.OpRISCV64CALLtailinter:
   522  		s.TailCall(v)
   523  	case ssaop.OpRISCV64LoweredWB:
   524  		p := s.Prog(obj.ACALL)
   525  		p.To.Type = obj.TYPE_MEM
   526  		p.To.Name = obj.NAME_EXTERN
   527  		// AuxInt encodes how many buffer entries we need.
   528  		p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1]
   529  
   530  	case ssaop.OpRISCV64LoweredPanicBoundsRR, ssaop.OpRISCV64LoweredPanicBoundsRC, ssaop.OpRISCV64LoweredPanicBoundsCR, ssaop.OpRISCV64LoweredPanicBoundsCC:
   531  		// Compute the constant we put in the PCData entry for this call.
   532  		code, signed := ssa.BoundsKind(v.AuxInt).Code()
   533  		xIsReg := false
   534  		yIsReg := false
   535  		xVal := 0
   536  		yVal := 0
   537  		switch v.Op {
   538  		case ssaop.OpRISCV64LoweredPanicBoundsRR:
   539  			xIsReg = true
   540  			xVal = int(v.Args[0].Reg() - riscv.REG_X5)
   541  			yIsReg = true
   542  			yVal = int(v.Args[1].Reg() - riscv.REG_X5)
   543  		case ssaop.OpRISCV64LoweredPanicBoundsRC:
   544  			xIsReg = true
   545  			xVal = int(v.Args[0].Reg() - riscv.REG_X5)
   546  			c := v.Aux.(ssa.PanicBoundsC).C
   547  			if c >= 0 && c <= abi.BoundsMaxConst {
   548  				yVal = int(c)
   549  			} else {
   550  				// Move constant to a register
   551  				yIsReg = true
   552  				if yVal == xVal {
   553  					yVal = 1
   554  				}
   555  				p := s.Prog(riscv.AMOV)
   556  				p.From.Type = obj.TYPE_CONST
   557  				p.From.Offset = c
   558  				p.To.Type = obj.TYPE_REG
   559  				p.To.Reg = riscv.REG_X5 + int16(yVal)
   560  			}
   561  		case ssaop.OpRISCV64LoweredPanicBoundsCR:
   562  			yIsReg = true
   563  			yVal = int(v.Args[0].Reg() - riscv.REG_X5)
   564  			c := v.Aux.(ssa.PanicBoundsC).C
   565  			if c >= 0 && c <= abi.BoundsMaxConst {
   566  				xVal = int(c)
   567  			} else {
   568  				// Move constant to a register
   569  				if xVal == yVal {
   570  					xVal = 1
   571  				}
   572  				p := s.Prog(riscv.AMOV)
   573  				p.From.Type = obj.TYPE_CONST
   574  				p.From.Offset = c
   575  				p.To.Type = obj.TYPE_REG
   576  				p.To.Reg = riscv.REG_X5 + int16(xVal)
   577  			}
   578  		case ssaop.OpRISCV64LoweredPanicBoundsCC:
   579  			c := v.Aux.(ssa.PanicBoundsCC).Cx
   580  			if c >= 0 && c <= abi.BoundsMaxConst {
   581  				xVal = int(c)
   582  			} else {
   583  				// Move constant to a register
   584  				xIsReg = true
   585  				p := s.Prog(riscv.AMOV)
   586  				p.From.Type = obj.TYPE_CONST
   587  				p.From.Offset = c
   588  				p.To.Type = obj.TYPE_REG
   589  				p.To.Reg = riscv.REG_X5 + int16(xVal)
   590  			}
   591  			c = v.Aux.(ssa.PanicBoundsCC).Cy
   592  			if c >= 0 && c <= abi.BoundsMaxConst {
   593  				yVal = int(c)
   594  			} else {
   595  				// Move constant to a register
   596  				yIsReg = true
   597  				yVal = 1
   598  				p := s.Prog(riscv.AMOV)
   599  				p.From.Type = obj.TYPE_CONST
   600  				p.From.Offset = c
   601  				p.To.Type = obj.TYPE_REG
   602  				p.To.Reg = riscv.REG_X5 + int16(yVal)
   603  			}
   604  		}
   605  		c := abi.BoundsEncode(code, signed, xIsReg, yIsReg, xVal, yVal)
   606  
   607  		p := s.Prog(obj.APCDATA)
   608  		p.From.SetConst(abi.PCDATA_PanicBounds)
   609  		p.To.SetConst(int64(c))
   610  		p = s.Prog(obj.ACALL)
   611  		p.To.Type = obj.TYPE_MEM
   612  		p.To.Name = obj.NAME_EXTERN
   613  		p.To.Sym = ir.Syms.PanicBounds
   614  
   615  	case ssaop.OpRISCV64LoweredAtomicLoad8:
   616  		p1 := s.Prog(riscv.AFENCE)
   617  		p1.From.Type = obj.TYPE_SPECIAL
   618  		p1.From.Offset = int64(riscv.SPOP_FENCE_RW)
   619  		p1.To.Type = obj.TYPE_SPECIAL
   620  		p1.To.Offset = int64(riscv.SPOP_FENCE_RW)
   621  
   622  		p := s.Prog(riscv.AMOVBU)
   623  		p.From.Type = obj.TYPE_MEM
   624  		p.From.Reg = v.Args[0].Reg()
   625  		p.To.Type = obj.TYPE_REG
   626  		p.To.Reg = v.Reg0()
   627  
   628  		p2 := s.Prog(riscv.AFENCE)
   629  		p2.From.Type = obj.TYPE_SPECIAL
   630  		p2.From.Offset = int64(riscv.SPOP_FENCE_R)
   631  		p2.To.Type = obj.TYPE_SPECIAL
   632  		p2.To.Offset = int64(riscv.SPOP_FENCE_RW)
   633  
   634  	case ssaop.OpRISCV64LoweredAtomicLoad32, ssaop.OpRISCV64LoweredAtomicLoad64:
   635  		as := riscv.ALRW
   636  		if v.Op == ssaop.OpRISCV64LoweredAtomicLoad64 {
   637  			as = riscv.ALRD
   638  		}
   639  		p := s.Prog(as)
   640  		p.From.Type = obj.TYPE_MEM
   641  		p.From.Reg = v.Args[0].Reg()
   642  		p.To.Type = obj.TYPE_REG
   643  		p.To.Reg = v.Reg0()
   644  
   645  	case ssaop.OpRISCV64LoweredAtomicStore8:
   646  		p1 := s.Prog(riscv.AFENCE)
   647  		p1.From.Type = obj.TYPE_SPECIAL
   648  		p1.From.Offset = int64(riscv.SPOP_FENCE_RW)
   649  		p1.To.Type = obj.TYPE_SPECIAL
   650  		p1.To.Offset = int64(riscv.SPOP_FENCE_W)
   651  
   652  		p := s.Prog(riscv.AMOVB)
   653  		p.From.Type = obj.TYPE_REG
   654  		p.From.Reg = v.Args[1].Reg()
   655  		p.To.Type = obj.TYPE_MEM
   656  		p.To.Reg = v.Args[0].Reg()
   657  
   658  		p2 := s.Prog(riscv.AFENCE)
   659  		p2.From.Type = obj.TYPE_SPECIAL
   660  		p2.From.Offset = int64(riscv.SPOP_FENCE_RW)
   661  		p2.To.Type = obj.TYPE_SPECIAL
   662  		p2.To.Offset = int64(riscv.SPOP_FENCE_RW)
   663  
   664  	case ssaop.OpRISCV64LoweredAtomicStore32, ssaop.OpRISCV64LoweredAtomicStore64:
   665  		as := riscv.AAMOSWAPW
   666  		if v.Op == ssaop.OpRISCV64LoweredAtomicStore64 {
   667  			as = riscv.AAMOSWAPD
   668  		}
   669  		p := s.Prog(as)
   670  		p.From.Type = obj.TYPE_REG
   671  		p.From.Reg = v.Args[1].Reg()
   672  		p.To.Type = obj.TYPE_MEM
   673  		p.To.Reg = v.Args[0].Reg()
   674  		p.RegTo2 = riscv.REG_ZERO
   675  
   676  	case ssaop.OpRISCV64LoweredAtomicAdd32, ssaop.OpRISCV64LoweredAtomicAdd64:
   677  		as := riscv.AAMOADDW
   678  		if v.Op == ssaop.OpRISCV64LoweredAtomicAdd64 {
   679  			as = riscv.AAMOADDD
   680  		}
   681  		p := s.Prog(as)
   682  		p.From.Type = obj.TYPE_REG
   683  		p.From.Reg = v.Args[1].Reg()
   684  		p.To.Type = obj.TYPE_MEM
   685  		p.To.Reg = v.Args[0].Reg()
   686  		p.RegTo2 = riscv.REG_TMP
   687  
   688  		p2 := s.Prog(riscv.AADD)
   689  		p2.From.Type = obj.TYPE_REG
   690  		p2.From.Reg = riscv.REG_TMP
   691  		p2.Reg = v.Args[1].Reg()
   692  		p2.To.Type = obj.TYPE_REG
   693  		p2.To.Reg = v.Reg0()
   694  
   695  	case ssaop.OpRISCV64LoweredAtomicExchange32, ssaop.OpRISCV64LoweredAtomicExchange64:
   696  		as := riscv.AAMOSWAPW
   697  		if v.Op == ssaop.OpRISCV64LoweredAtomicExchange64 {
   698  			as = riscv.AAMOSWAPD
   699  		}
   700  		p := s.Prog(as)
   701  		p.From.Type = obj.TYPE_REG
   702  		p.From.Reg = v.Args[1].Reg()
   703  		p.To.Type = obj.TYPE_MEM
   704  		p.To.Reg = v.Args[0].Reg()
   705  		p.RegTo2 = v.Reg0()
   706  
   707  	case ssaop.OpRISCV64LoweredAtomicCas32, ssaop.OpRISCV64LoweredAtomicCas64:
   708  		// MOV  ZERO, Rout
   709  		// LR	(Rarg0), Rtmp
   710  		// BNE	Rtmp, Rarg1, 3(PC)
   711  		// SC	Rarg2, (Rarg0), Rtmp
   712  		// BNE	Rtmp, ZERO, -3(PC)
   713  		// MOV	$1, Rout
   714  
   715  		lr := riscv.ALRW
   716  		sc := riscv.ASCW
   717  		if v.Op == ssaop.OpRISCV64LoweredAtomicCas64 {
   718  			lr = riscv.ALRD
   719  			sc = riscv.ASCD
   720  		}
   721  
   722  		r0 := v.Args[0].Reg()
   723  		r1 := v.Args[1].Reg()
   724  		r2 := v.Args[2].Reg()
   725  		out := v.Reg0()
   726  
   727  		p := s.Prog(riscv.AMOV)
   728  		p.From.Type = obj.TYPE_REG
   729  		p.From.Reg = riscv.REG_ZERO
   730  		p.To.Type = obj.TYPE_REG
   731  		p.To.Reg = out
   732  
   733  		p1 := s.Prog(lr)
   734  		p1.From.Type = obj.TYPE_MEM
   735  		p1.From.Reg = r0
   736  		p1.To.Type = obj.TYPE_REG
   737  		p1.To.Reg = riscv.REG_TMP
   738  
   739  		p2 := s.Prog(riscv.ABNE)
   740  		p2.From.Type = obj.TYPE_REG
   741  		p2.From.Reg = r1
   742  		p2.Reg = riscv.REG_TMP
   743  		p2.To.Type = obj.TYPE_BRANCH
   744  
   745  		p3 := s.Prog(sc)
   746  		p3.From.Type = obj.TYPE_REG
   747  		p3.From.Reg = r2
   748  		p3.To.Type = obj.TYPE_MEM
   749  		p3.To.Reg = r0
   750  		p3.RegTo2 = riscv.REG_TMP
   751  
   752  		p4 := s.Prog(riscv.ABNE)
   753  		p4.From.Type = obj.TYPE_REG
   754  		p4.From.Reg = riscv.REG_TMP
   755  		p4.Reg = riscv.REG_ZERO
   756  		p4.To.Type = obj.TYPE_BRANCH
   757  		p4.To.SetTarget(p1)
   758  
   759  		p5 := s.Prog(riscv.AMOV)
   760  		p5.From.Type = obj.TYPE_CONST
   761  		p5.From.Offset = 1
   762  		p5.To.Type = obj.TYPE_REG
   763  		p5.To.Reg = out
   764  
   765  		p6 := s.Prog(obj.ANOP)
   766  		p2.To.SetTarget(p6)
   767  
   768  	case ssaop.OpRISCV64LoweredAtomicAnd32, ssaop.OpRISCV64LoweredAtomicOr32:
   769  		p := s.Prog(v.Op.Asm())
   770  		p.From.Type = obj.TYPE_REG
   771  		p.From.Reg = v.Args[1].Reg()
   772  		p.To.Type = obj.TYPE_MEM
   773  		p.To.Reg = v.Args[0].Reg()
   774  		p.RegTo2 = riscv.REG_ZERO
   775  
   776  	case ssaop.OpRISCV64LoweredAtomicAnd32value, ssaop.OpRISCV64LoweredAtomicAnd64value,
   777  		ssaop.OpRISCV64LoweredAtomicOr32value, ssaop.OpRISCV64LoweredAtomicOr64value:
   778  		p := s.Prog(v.Op.Asm())
   779  		p.From.Type = obj.TYPE_REG
   780  		p.From.Reg = v.Args[1].Reg()
   781  		p.To.Type = obj.TYPE_MEM
   782  		p.To.Reg = v.Args[0].Reg()
   783  		p.RegTo2 = v.Reg0()
   784  
   785  	case ssaop.OpRISCV64LoweredZero:
   786  		ptr := v.Args[0].Reg()
   787  		n, align := v.AuxSizeAndAlign()
   788  		mov, sz := largestMove(align)
   789  
   790  		// mov	ZERO, (offset)(Rarg0)
   791  		var off int64
   792  		for n >= sz {
   793  			zeroOp(s, mov, ptr, off)
   794  			off += sz
   795  			n -= sz
   796  		}
   797  
   798  		for i := len(fracMovOps) - 1; i >= 0; i-- {
   799  			tsz := int64(1 << i)
   800  			if n < tsz {
   801  				continue
   802  			}
   803  			zeroOp(s, fracMovOps[i], ptr, off)
   804  			off += tsz
   805  			n -= tsz
   806  		}
   807  
   808  	case ssaop.OpRISCV64LoweredZeroLoop:
   809  		ptr := v.Args[0].Reg()
   810  		n, align := v.AuxSizeAndAlign()
   811  		mov, sz := largestMove(align)
   812  		chunk := 8 * sz
   813  
   814  		if n <= 3*chunk {
   815  			v.Fatalf("ZeroLoop too small:%d, expect:%d", n, 3*chunk)
   816  		}
   817  
   818  		tmp := v.RegTmp()
   819  
   820  		if n >= 1<<31 {
   821  			p := s.Prog(riscv.AMOV)
   822  			p.From.Type = obj.TYPE_CONST
   823  			p.From.Offset = n - n%chunk
   824  			p.To.Type = obj.TYPE_REG
   825  			p.To.Reg = tmp
   826  		}
   827  		p := s.Prog(riscv.AADD)
   828  		if n >= 1<<31 {
   829  			p.From.Type = obj.TYPE_REG
   830  			p.From.Reg = tmp
   831  		} else {
   832  			p.From.Type = obj.TYPE_CONST
   833  			p.From.Offset = n - n%chunk
   834  		}
   835  		p.Reg = ptr
   836  		p.To.Type = obj.TYPE_REG
   837  		p.To.Reg = tmp
   838  
   839  		for i := int64(0); i < 8; i++ {
   840  			zeroOp(s, mov, ptr, sz*i)
   841  		}
   842  
   843  		p2 := s.Prog(riscv.AADD)
   844  		p2.From.Type = obj.TYPE_CONST
   845  		p2.From.Offset = chunk
   846  		p2.To.Type = obj.TYPE_REG
   847  		p2.To.Reg = ptr
   848  
   849  		p3 := s.Prog(riscv.ABNE)
   850  		p3.From.Reg = tmp
   851  		p3.From.Type = obj.TYPE_REG
   852  		p3.Reg = ptr
   853  		p3.To.Type = obj.TYPE_BRANCH
   854  		p3.To.SetTarget(p.Link)
   855  
   856  		n %= chunk
   857  
   858  		// mov	ZERO, (offset)(Rarg0)
   859  		var off int64
   860  		for n >= sz {
   861  			zeroOp(s, mov, ptr, off)
   862  			off += sz
   863  			n -= sz
   864  		}
   865  
   866  		for i := len(fracMovOps) - 1; i >= 0; i-- {
   867  			tsz := int64(1 << i)
   868  			if n < tsz {
   869  				continue
   870  			}
   871  			zeroOp(s, fracMovOps[i], ptr, off)
   872  			off += tsz
   873  			n -= tsz
   874  		}
   875  
   876  	case ssaop.OpRISCV64LoweredMove:
   877  		dst := v.Args[0].Reg()
   878  		src := v.Args[1].Reg()
   879  		if dst == src {
   880  			break
   881  		}
   882  
   883  		n, align := v.AuxSizeAndAlign()
   884  		mov, sz := largestMove(align)
   885  
   886  		var off int64
   887  		tmp := int16(riscv.REG_X5)
   888  		for n >= sz {
   889  			moveOp(s, mov, dst, src, tmp, off)
   890  			off += sz
   891  			n -= sz
   892  		}
   893  
   894  		for i := len(fracMovOps) - 1; i >= 0; i-- {
   895  			tsz := int64(1 << i)
   896  			if n < tsz {
   897  				continue
   898  			}
   899  			moveOp(s, fracMovOps[i], dst, src, tmp, off)
   900  			off += tsz
   901  			n -= tsz
   902  		}
   903  
   904  	case ssaop.OpRISCV64LoweredMoveLoop:
   905  		dst := v.Args[0].Reg()
   906  		src := v.Args[1].Reg()
   907  		if dst == src {
   908  			break
   909  		}
   910  
   911  		n, align := v.AuxSizeAndAlign()
   912  		mov, sz := largestMove(align)
   913  		chunk := 8 * sz
   914  
   915  		if n <= 3*chunk {
   916  			v.Fatalf("MoveLoop too small:%d, expect:%d", n, 3*chunk)
   917  		}
   918  		tmp := int16(riscv.REG_X5)
   919  
   920  		if n >= 1<<31 {
   921  			p := s.Prog(riscv.AMOV)
   922  			p.From.Type = obj.TYPE_CONST
   923  			p.From.Offset = n - n%chunk
   924  			p.To.Type = obj.TYPE_REG
   925  			p.To.Reg = riscv.REG_X6
   926  		}
   927  		p := s.Prog(riscv.AADD)
   928  		if n >= 1<<31 {
   929  			p.From.Type = obj.TYPE_REG
   930  			p.From.Reg = riscv.REG_X6
   931  		} else {
   932  			p.From.Type = obj.TYPE_CONST
   933  			p.From.Offset = n - n%chunk
   934  		}
   935  		p.Reg = src
   936  		p.To.Type = obj.TYPE_REG
   937  		p.To.Reg = riscv.REG_X6
   938  
   939  		for i := int64(0); i < 8; i++ {
   940  			moveOp(s, mov, dst, src, tmp, sz*i)
   941  		}
   942  
   943  		p1 := s.Prog(riscv.AADD)
   944  		p1.From.Type = obj.TYPE_CONST
   945  		p1.From.Offset = chunk
   946  		p1.To.Type = obj.TYPE_REG
   947  		p1.To.Reg = src
   948  
   949  		p2 := s.Prog(riscv.AADD)
   950  		p2.From.Type = obj.TYPE_CONST
   951  		p2.From.Offset = chunk
   952  		p2.To.Type = obj.TYPE_REG
   953  		p2.To.Reg = dst
   954  
   955  		p3 := s.Prog(riscv.ABNE)
   956  		p3.From.Reg = riscv.REG_X6
   957  		p3.From.Type = obj.TYPE_REG
   958  		p3.Reg = src
   959  		p3.To.Type = obj.TYPE_BRANCH
   960  		p3.To.SetTarget(p.Link)
   961  
   962  		n %= chunk
   963  
   964  		var off int64
   965  		for n >= sz {
   966  			moveOp(s, mov, dst, src, tmp, off)
   967  			off += sz
   968  			n -= sz
   969  		}
   970  
   971  		for i := len(fracMovOps) - 1; i >= 0; i-- {
   972  			tsz := int64(1 << i)
   973  			if n < tsz {
   974  				continue
   975  			}
   976  			moveOp(s, fracMovOps[i], dst, src, tmp, off)
   977  			off += tsz
   978  			n -= tsz
   979  		}
   980  
   981  	case ssaop.OpRISCV64LoweredNilCheck:
   982  		// Issue a load which will fault if arg is nil.
   983  		p := s.Prog(riscv.AMOVB)
   984  		p.From.Type = obj.TYPE_MEM
   985  		p.From.Reg = v.Args[0].Reg()
   986  		ssagen.AddAux(&p.From, v)
   987  		p.To.Type = obj.TYPE_REG
   988  		p.To.Reg = riscv.REG_ZERO
   989  		if logopt.Enabled() {
   990  			logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
   991  		}
   992  		if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos == 1 in generated wrappers
   993  			base.WarnfAt(v.Pos, "generated nil check")
   994  		}
   995  
   996  	case ssaop.OpRISCV64LoweredGetClosurePtr:
   997  		// Closure pointer is S10 (riscv.REG_CTXT).
   998  		ssagen.CheckLoweredGetClosurePtr(v)
   999  
  1000  	case ssaop.OpRISCV64LoweredGetCallerSP:
  1001  		// caller's SP is FixedFrameSize below the address of the first arg
  1002  		p := s.Prog(riscv.AMOV)
  1003  		p.From.Type = obj.TYPE_ADDR
  1004  		p.From.Offset = -base.Ctxt.Arch.FixedFrameSize
  1005  		p.From.Name = obj.NAME_PARAM
  1006  		p.To.Type = obj.TYPE_REG
  1007  		p.To.Reg = v.Reg()
  1008  
  1009  	case ssaop.OpRISCV64LoweredGetCallerPC:
  1010  		p := s.Prog(obj.AGETCALLERPC)
  1011  		p.To.Type = obj.TYPE_REG
  1012  		p.To.Reg = v.Reg()
  1013  
  1014  	case ssaop.OpRISCV64LoweredPubBarrier:
  1015  		// FENCE W, W
  1016  		p := s.Prog(v.Op.Asm())
  1017  		p.From.Type = obj.TYPE_SPECIAL
  1018  		p.From.Offset = int64(riscv.SPOP_FENCE_W)
  1019  		p.To.Type = obj.TYPE_SPECIAL
  1020  		p.To.Offset = int64(riscv.SPOP_FENCE_W)
  1021  
  1022  	case ssaop.OpRISCV64LoweredRound32F, ssaop.OpRISCV64LoweredRound64F:
  1023  		// input is already rounded
  1024  
  1025  	case ssaop.OpClobber, ssaop.OpClobberReg:
  1026  		// TODO: implement for clobberdead experiment. Nop is ok for now.
  1027  
  1028  	default:
  1029  		v.Fatalf("Unhandled op %v", v.Op)
  1030  	}
  1031  }
  1032  
  1033  var blockBranch = [...]obj.As{
  1034  	block.BlockRISCV64BEQ:  riscv.ABEQ,
  1035  	block.BlockRISCV64BEQZ: riscv.ABEQZ,
  1036  	block.BlockRISCV64BGE:  riscv.ABGE,
  1037  	block.BlockRISCV64BGEU: riscv.ABGEU,
  1038  	block.BlockRISCV64BGEZ: riscv.ABGEZ,
  1039  	block.BlockRISCV64BGTZ: riscv.ABGTZ,
  1040  	block.BlockRISCV64BLEZ: riscv.ABLEZ,
  1041  	block.BlockRISCV64BLT:  riscv.ABLT,
  1042  	block.BlockRISCV64BLTU: riscv.ABLTU,
  1043  	block.BlockRISCV64BLTZ: riscv.ABLTZ,
  1044  	block.BlockRISCV64BNE:  riscv.ABNE,
  1045  	block.BlockRISCV64BNEZ: riscv.ABNEZ,
  1046  }
  1047  
  1048  func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
  1049  	s.SetPos(b.Pos)
  1050  
  1051  	switch b.Kind {
  1052  	case block.BlockPlain, block.BlockDefer:
  1053  		if b.Succs[0].Block() != next {
  1054  			p := s.Prog(obj.AJMP)
  1055  			p.To.Type = obj.TYPE_BRANCH
  1056  			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
  1057  		}
  1058  	case block.BlockExit, block.BlockRetJmp:
  1059  	case block.BlockRet:
  1060  		s.Prog(obj.ARET)
  1061  	case block.BlockRISCV64BEQ, block.BlockRISCV64BEQZ, block.BlockRISCV64BNE, block.BlockRISCV64BNEZ,
  1062  		block.BlockRISCV64BLT, block.BlockRISCV64BLEZ, block.BlockRISCV64BGE, block.BlockRISCV64BGEZ,
  1063  		block.BlockRISCV64BLTZ, block.BlockRISCV64BGTZ, block.BlockRISCV64BLTU, block.BlockRISCV64BGEU:
  1064  
  1065  		as := blockBranch[b.Kind]
  1066  		invAs := riscv.InvertBranch(as)
  1067  
  1068  		var p *obj.Prog
  1069  		switch next {
  1070  		case b.Succs[0].Block():
  1071  			p = s.Br(invAs, b.Succs[1].Block())
  1072  		case b.Succs[1].Block():
  1073  			p = s.Br(as, b.Succs[0].Block())
  1074  		default:
  1075  			if b.Likely != ssa.BranchUnlikely {
  1076  				p = s.Br(as, b.Succs[0].Block())
  1077  				s.Br(obj.AJMP, b.Succs[1].Block())
  1078  			} else {
  1079  				p = s.Br(invAs, b.Succs[1].Block())
  1080  				s.Br(obj.AJMP, b.Succs[0].Block())
  1081  			}
  1082  		}
  1083  
  1084  		p.From.Type = obj.TYPE_REG
  1085  		switch b.Kind {
  1086  		case block.BlockRISCV64BEQ, block.BlockRISCV64BNE, block.BlockRISCV64BLT, block.BlockRISCV64BGE, block.BlockRISCV64BLTU, block.BlockRISCV64BGEU:
  1087  			if b.NumControls() != 2 {
  1088  				b.Fatalf("Unexpected number of controls (%d != 2): %s", b.NumControls(), b.LongString())
  1089  			}
  1090  			p.From.Reg = b.Controls[0].Reg()
  1091  			p.Reg = b.Controls[1].Reg()
  1092  
  1093  		case block.BlockRISCV64BEQZ, block.BlockRISCV64BNEZ, block.BlockRISCV64BGEZ, block.BlockRISCV64BLEZ, block.BlockRISCV64BLTZ, block.BlockRISCV64BGTZ:
  1094  			if b.NumControls() != 1 {
  1095  				b.Fatalf("Unexpected number of controls (%d != 1): %s", b.NumControls(), b.LongString())
  1096  			}
  1097  			p.From.Reg = b.Controls[0].Reg()
  1098  		}
  1099  
  1100  	case block.BlockRISCV64JUMPTABLE:
  1101  		// Jump table:
  1102  		// TMP = base + index*8 (SH3ADD if Zba else SLLI+ADD).
  1103  		// Load slot into TMP, then indirect JMP through TMP.
  1104  		var p *obj.Prog
  1105  		if buildcfg.GORISCV64 >= 22 {
  1106  			p = s.Prog(riscv.ASH3ADD)
  1107  			p.From.Type = obj.TYPE_REG
  1108  			p.From.Reg = b.Controls[1].Reg()
  1109  			p.Reg = b.Controls[0].Reg()
  1110  			p.To.Type = obj.TYPE_REG
  1111  			p.To.Reg = riscv.REG_TMP
  1112  		} else {
  1113  			p = s.Prog(riscv.ASLLI)
  1114  			p.From.Type = obj.TYPE_CONST
  1115  			p.From.Offset = 3
  1116  			p.Reg = b.Controls[0].Reg()
  1117  			p.To.Type = obj.TYPE_REG
  1118  			p.To.Reg = riscv.REG_TMP
  1119  
  1120  			p = s.Prog(riscv.AADD)
  1121  			p.From.Type = obj.TYPE_REG
  1122  			p.From.Reg = riscv.REG_TMP
  1123  			p.Reg = b.Controls[1].Reg()
  1124  			p.To.Type = obj.TYPE_REG
  1125  			p.To.Reg = riscv.REG_TMP
  1126  		}
  1127  
  1128  		p = s.Prog(riscv.AMOV)
  1129  		p.From.Type = obj.TYPE_MEM
  1130  		p.From.Reg = riscv.REG_TMP
  1131  		p.To.Type = obj.TYPE_REG
  1132  		p.To.Reg = riscv.REG_TMP
  1133  
  1134  		p = s.Prog(obj.AJMP)
  1135  		p.To.Type = obj.TYPE_MEM
  1136  		p.To.Reg = riscv.REG_TMP
  1137  		// Save jump tables for later resolution of the target blocks.
  1138  		s.JumpTables = append(s.JumpTables, b)
  1139  
  1140  	default:
  1141  		b.Fatalf("Unhandled block: %s", b.LongString())
  1142  	}
  1143  }
  1144  
  1145  func loadRegResult(s *ssagen.State, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog {
  1146  	p := s.Prog(loadByType(t))
  1147  	p.From.Type = obj.TYPE_MEM
  1148  	p.From.Name = obj.NAME_AUTO
  1149  	p.From.Sym = n.Linksym()
  1150  	p.From.Offset = n.FrameOffset() + off
  1151  	p.To.Type = obj.TYPE_REG
  1152  	p.To.Reg = reg
  1153  	return p
  1154  }
  1155  
  1156  func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog {
  1157  	p = pp.Append(p, storeByType(t), obj.TYPE_REG, reg, 0, obj.TYPE_MEM, 0, n.FrameOffset()+off)
  1158  	p.To.Name = obj.NAME_PARAM
  1159  	p.To.Sym = n.Linksym()
  1160  	p.Pos = p.Pos.WithNotStmt()
  1161  	return p
  1162  }
  1163  
  1164  func zeroOp(s *ssagen.State, mov obj.As, reg int16, off int64) {
  1165  	p := s.Prog(mov)
  1166  	p.From.Type = obj.TYPE_REG
  1167  	p.From.Reg = riscv.REG_ZERO
  1168  	p.To.Type = obj.TYPE_MEM
  1169  	p.To.Reg = reg
  1170  	p.To.Offset = off
  1171  	return
  1172  }
  1173  
  1174  func moveOp(s *ssagen.State, mov obj.As, dst int16, src int16, tmp int16, off int64) {
  1175  	p := s.Prog(mov)
  1176  	p.From.Type = obj.TYPE_MEM
  1177  	p.From.Reg = src
  1178  	p.From.Offset = off
  1179  	p.To.Type = obj.TYPE_REG
  1180  	p.To.Reg = tmp
  1181  
  1182  	p1 := s.Prog(mov)
  1183  	p1.From.Type = obj.TYPE_REG
  1184  	p1.From.Reg = tmp
  1185  	p1.To.Type = obj.TYPE_MEM
  1186  	p1.To.Reg = dst
  1187  	p1.To.Offset = off
  1188  
  1189  	return
  1190  }
  1191  

View as plain text