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

     1  // Copyright 2022 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 loong64
     6  
     7  import (
     8  	"math"
     9  
    10  	"cmd/compile/internal/base"
    11  	"cmd/compile/internal/ir"
    12  	"cmd/compile/internal/logopt"
    13  	"cmd/compile/internal/objw"
    14  	"cmd/compile/internal/ssa"
    15  	"cmd/compile/internal/ssagen"
    16  	"cmd/compile/internal/types"
    17  	"cmd/internal/obj"
    18  	"cmd/internal/obj/loong64"
    19  	"internal/abi"
    20  )
    21  
    22  // isFPreg reports whether r is an FP register.
    23  func isFPreg(r int16) bool {
    24  	return loong64.REG_F0 <= r && r <= loong64.REG_F31
    25  }
    26  
    27  // loadByType returns the load instruction of the given type.
    28  func loadByType(t *types.Type, r int16) obj.As {
    29  	if isFPreg(r) {
    30  		if t.Size() == 4 {
    31  			return loong64.AMOVF
    32  		} else {
    33  			return loong64.AMOVD
    34  		}
    35  	} else {
    36  		switch t.Size() {
    37  		case 1:
    38  			if t.IsSigned() {
    39  				return loong64.AMOVB
    40  			} else {
    41  				return loong64.AMOVBU
    42  			}
    43  		case 2:
    44  			if t.IsSigned() {
    45  				return loong64.AMOVH
    46  			} else {
    47  				return loong64.AMOVHU
    48  			}
    49  		case 4:
    50  			if t.IsSigned() {
    51  				return loong64.AMOVW
    52  			} else {
    53  				return loong64.AMOVWU
    54  			}
    55  		case 8:
    56  			return loong64.AMOVV
    57  		}
    58  	}
    59  	panic("bad load type")
    60  }
    61  
    62  // storeByType returns the store instruction of the given type.
    63  func storeByType(t *types.Type, r int16) obj.As {
    64  	if isFPreg(r) {
    65  		if t.Size() == 4 {
    66  			return loong64.AMOVF
    67  		} else {
    68  			return loong64.AMOVD
    69  		}
    70  	} else {
    71  		switch t.Size() {
    72  		case 1:
    73  			return loong64.AMOVB
    74  		case 2:
    75  			return loong64.AMOVH
    76  		case 4:
    77  			return loong64.AMOVW
    78  		case 8:
    79  			return loong64.AMOVV
    80  		}
    81  	}
    82  	panic("bad store type")
    83  }
    84  
    85  // largestMove returns the largest move instruction possible and its size,
    86  // given the alignment of the total size of the move.
    87  //
    88  // e.g., a 16-byte move may use MOVV, but an 11-byte move must use MOVB.
    89  //
    90  // Note that the moves may not be on naturally aligned addresses depending on
    91  // the source and destination.
    92  //
    93  // This matches the calculation in ssa.moveSize.
    94  func largestMove(alignment int64) (obj.As, int64) {
    95  	switch {
    96  	case alignment%8 == 0:
    97  		return loong64.AMOVV, 8
    98  	case alignment%4 == 0:
    99  		return loong64.AMOVW, 4
   100  	case alignment%2 == 0:
   101  		return loong64.AMOVH, 2
   102  	default:
   103  		return loong64.AMOVB, 1
   104  	}
   105  }
   106  
   107  func ssaGenValue(s *ssagen.State, v *ssa.Value) {
   108  	switch v.Op {
   109  	case ssa.OpCopy, ssa.OpLOONG64MOVVreg:
   110  		if v.Type.IsMemory() {
   111  			return
   112  		}
   113  		x := v.Args[0].Reg()
   114  		y := v.Reg()
   115  		if x == y {
   116  			return
   117  		}
   118  		as := loong64.AMOVV
   119  		if isFPreg(x) && isFPreg(y) {
   120  			as = loong64.AMOVD
   121  		}
   122  		p := s.Prog(as)
   123  		p.From.Type = obj.TYPE_REG
   124  		p.From.Reg = x
   125  		p.To.Type = obj.TYPE_REG
   126  		p.To.Reg = y
   127  	case ssa.OpLOONG64MOVVnop,
   128  		ssa.OpLOONG64ZERO,
   129  		ssa.OpLOONG64LoweredRound32F,
   130  		ssa.OpLOONG64LoweredRound64F:
   131  		// nothing to do
   132  	case ssa.OpLoadReg:
   133  		if v.Type.IsFlags() {
   134  			v.Fatalf("load flags not implemented: %v", v.LongString())
   135  			return
   136  		}
   137  		r := v.Reg()
   138  		p := s.Prog(loadByType(v.Type, r))
   139  		ssagen.AddrAuto(&p.From, v.Args[0])
   140  		p.To.Type = obj.TYPE_REG
   141  		p.To.Reg = r
   142  	case ssa.OpStoreReg:
   143  		if v.Type.IsFlags() {
   144  			v.Fatalf("store flags not implemented: %v", v.LongString())
   145  			return
   146  		}
   147  		r := v.Args[0].Reg()
   148  		p := s.Prog(storeByType(v.Type, r))
   149  		p.From.Type = obj.TYPE_REG
   150  		p.From.Reg = r
   151  		ssagen.AddrAuto(&p.To, v)
   152  	case ssa.OpArgIntReg, ssa.OpArgFloatReg:
   153  		// The assembler needs to wrap the entry safepoint/stack growth code with spill/unspill
   154  		// The loop only runs once.
   155  		for _, a := range v.Block.Func.RegArgs {
   156  			// Pass the spill/unspill information along to the assembler, offset by size of
   157  			// the saved LR slot.
   158  			addr := ssagen.SpillSlotAddr(a, loong64.REGSP, base.Ctxt.Arch.FixedFrameSize)
   159  			s.FuncInfo().AddSpill(
   160  				obj.RegSpill{Reg: a.Reg, Addr: addr, Unspill: loadByType(a.Type, a.Reg), Spill: storeByType(a.Type, a.Reg)})
   161  		}
   162  		v.Block.Func.RegArgs = nil
   163  		ssagen.CheckArgReg(v)
   164  	case ssa.OpLOONG64ADDV,
   165  		ssa.OpLOONG64SUBV,
   166  		ssa.OpLOONG64AND,
   167  		ssa.OpLOONG64OR,
   168  		ssa.OpLOONG64XOR,
   169  		ssa.OpLOONG64NOR,
   170  		ssa.OpLOONG64ANDN,
   171  		ssa.OpLOONG64ORN,
   172  		ssa.OpLOONG64SLL,
   173  		ssa.OpLOONG64SLLV,
   174  		ssa.OpLOONG64SRL,
   175  		ssa.OpLOONG64SRLV,
   176  		ssa.OpLOONG64SRA,
   177  		ssa.OpLOONG64SRAV,
   178  		ssa.OpLOONG64ROTR,
   179  		ssa.OpLOONG64ROTRV,
   180  		ssa.OpLOONG64ADDF,
   181  		ssa.OpLOONG64ADDD,
   182  		ssa.OpLOONG64SUBF,
   183  		ssa.OpLOONG64SUBD,
   184  		ssa.OpLOONG64MULF,
   185  		ssa.OpLOONG64MULD,
   186  		ssa.OpLOONG64DIVF,
   187  		ssa.OpLOONG64DIVD,
   188  		ssa.OpLOONG64MULV, ssa.OpLOONG64MULHV, ssa.OpLOONG64MULHVU, ssa.OpLOONG64MULH, ssa.OpLOONG64MULHU,
   189  		ssa.OpLOONG64DIVV, ssa.OpLOONG64REMV, ssa.OpLOONG64DIVVU, ssa.OpLOONG64REMVU,
   190  		ssa.OpLOONG64FCOPYSGD:
   191  		p := s.Prog(v.Op.Asm())
   192  		p.From.Type = obj.TYPE_REG
   193  		p.From.Reg = v.Args[1].Reg()
   194  		p.Reg = v.Args[0].Reg()
   195  		p.To.Type = obj.TYPE_REG
   196  		p.To.Reg = v.Reg()
   197  
   198  	case ssa.OpLOONG64BSTRPICKV,
   199  		ssa.OpLOONG64BSTRPICKW:
   200  		p := s.Prog(v.Op.Asm())
   201  		p.From.Type = obj.TYPE_CONST
   202  		if v.Op == ssa.OpLOONG64BSTRPICKW {
   203  			p.From.Offset = v.AuxInt >> 5
   204  			p.AddRestSourceConst(v.AuxInt & 0x1f)
   205  		} else {
   206  			p.From.Offset = v.AuxInt >> 6
   207  			p.AddRestSourceConst(v.AuxInt & 0x3f)
   208  		}
   209  		p.Reg = v.Args[0].Reg()
   210  		p.To.Type = obj.TYPE_REG
   211  		p.To.Reg = v.Reg()
   212  
   213  	case ssa.OpLOONG64FMINF,
   214  		ssa.OpLOONG64FMIND,
   215  		ssa.OpLOONG64FMAXF,
   216  		ssa.OpLOONG64FMAXD:
   217  		// ADDD Rarg0, Rarg1, Rout
   218  		// CMPEQD Rarg0, Rarg0, FCC0
   219  		// bceqz FCC0, end
   220  		// CMPEQD Rarg1, Rarg1, FCC0
   221  		// bceqz FCC0, end
   222  		// F(MIN|MAX)(F|D)
   223  
   224  		r0 := v.Args[0].Reg()
   225  		r1 := v.Args[1].Reg()
   226  		out := v.Reg()
   227  		add, fcmp := loong64.AADDD, loong64.ACMPEQD
   228  		if v.Op == ssa.OpLOONG64FMINF || v.Op == ssa.OpLOONG64FMAXF {
   229  			add = loong64.AADDF
   230  			fcmp = loong64.ACMPEQF
   231  		}
   232  		p1 := s.Prog(add)
   233  		p1.From.Type = obj.TYPE_REG
   234  		p1.From.Reg = r0
   235  		p1.Reg = r1
   236  		p1.To.Type = obj.TYPE_REG
   237  		p1.To.Reg = out
   238  
   239  		p2 := s.Prog(fcmp)
   240  		p2.From.Type = obj.TYPE_REG
   241  		p2.From.Reg = r0
   242  		p2.Reg = r0
   243  		p2.To.Type = obj.TYPE_REG
   244  		p2.To.Reg = loong64.REG_FCC0
   245  
   246  		p3 := s.Prog(loong64.ABFPF)
   247  		p3.To.Type = obj.TYPE_BRANCH
   248  
   249  		p4 := s.Prog(fcmp)
   250  		p4.From.Type = obj.TYPE_REG
   251  		p4.From.Reg = r1
   252  		p4.Reg = r1
   253  		p4.To.Type = obj.TYPE_REG
   254  		p4.To.Reg = loong64.REG_FCC0
   255  
   256  		p5 := s.Prog(loong64.ABFPF)
   257  		p5.To.Type = obj.TYPE_BRANCH
   258  
   259  		p6 := s.Prog(v.Op.Asm())
   260  		p6.From.Type = obj.TYPE_REG
   261  		p6.From.Reg = r1
   262  		p6.Reg = r0
   263  		p6.To.Type = obj.TYPE_REG
   264  		p6.To.Reg = out
   265  
   266  		nop := s.Prog(obj.ANOP)
   267  		p3.To.SetTarget(nop)
   268  		p5.To.SetTarget(nop)
   269  
   270  	case ssa.OpLOONG64SGT,
   271  		ssa.OpLOONG64SGTU:
   272  		p := s.Prog(v.Op.Asm())
   273  		p.From.Type = obj.TYPE_REG
   274  		p.From.Reg = v.Args[0].Reg()
   275  		p.Reg = v.Args[1].Reg()
   276  		p.To.Type = obj.TYPE_REG
   277  		p.To.Reg = v.Reg()
   278  	case ssa.OpLOONG64ADDVconst,
   279  		ssa.OpLOONG64ADDV16const,
   280  		ssa.OpLOONG64SUBVconst,
   281  		ssa.OpLOONG64ANDconst,
   282  		ssa.OpLOONG64ORconst,
   283  		ssa.OpLOONG64XORconst,
   284  		ssa.OpLOONG64SLLconst,
   285  		ssa.OpLOONG64SLLVconst,
   286  		ssa.OpLOONG64SRLconst,
   287  		ssa.OpLOONG64SRLVconst,
   288  		ssa.OpLOONG64SRAconst,
   289  		ssa.OpLOONG64SRAVconst,
   290  		ssa.OpLOONG64ROTRconst,
   291  		ssa.OpLOONG64ROTRVconst,
   292  		ssa.OpLOONG64SGTconst,
   293  		ssa.OpLOONG64SGTUconst:
   294  		p := s.Prog(v.Op.Asm())
   295  		p.From.Type = obj.TYPE_CONST
   296  		p.From.Offset = v.AuxInt
   297  		p.Reg = v.Args[0].Reg()
   298  		p.To.Type = obj.TYPE_REG
   299  		p.To.Reg = v.Reg()
   300  
   301  	case ssa.OpLOONG64NORconst:
   302  		// MOVV $const, Rtmp
   303  		// NOR  Rtmp, Rarg0, Rout
   304  		p := s.Prog(loong64.AMOVV)
   305  		p.From.Type = obj.TYPE_CONST
   306  		p.From.Offset = v.AuxInt
   307  		p.To.Type = obj.TYPE_REG
   308  		p.To.Reg = loong64.REGTMP
   309  
   310  		p2 := s.Prog(v.Op.Asm())
   311  		p2.From.Type = obj.TYPE_REG
   312  		p2.From.Reg = loong64.REGTMP
   313  		p2.Reg = v.Args[0].Reg()
   314  		p2.To.Type = obj.TYPE_REG
   315  		p2.To.Reg = v.Reg()
   316  
   317  	case ssa.OpLOONG64MOVVconst:
   318  		r := v.Reg()
   319  		p := s.Prog(v.Op.Asm())
   320  		p.From.Type = obj.TYPE_CONST
   321  		p.From.Offset = v.AuxInt
   322  		p.To.Type = obj.TYPE_REG
   323  		p.To.Reg = r
   324  		if isFPreg(r) {
   325  			// cannot move into FP or special registers, use TMP as intermediate
   326  			p.To.Reg = loong64.REGTMP
   327  			p = s.Prog(loong64.AMOVV)
   328  			p.From.Type = obj.TYPE_REG
   329  			p.From.Reg = loong64.REGTMP
   330  			p.To.Type = obj.TYPE_REG
   331  			p.To.Reg = r
   332  		}
   333  	case ssa.OpLOONG64MOVFconst,
   334  		ssa.OpLOONG64MOVDconst:
   335  		p := s.Prog(v.Op.Asm())
   336  		p.From.Type = obj.TYPE_FCONST
   337  		p.From.Val = math.Float64frombits(uint64(v.AuxInt))
   338  		p.To.Type = obj.TYPE_REG
   339  		p.To.Reg = v.Reg()
   340  	case ssa.OpLOONG64CMPEQF,
   341  		ssa.OpLOONG64CMPEQD,
   342  		ssa.OpLOONG64CMPGEF,
   343  		ssa.OpLOONG64CMPGED,
   344  		ssa.OpLOONG64CMPGTF,
   345  		ssa.OpLOONG64CMPGTD:
   346  		p := s.Prog(v.Op.Asm())
   347  		p.From.Type = obj.TYPE_REG
   348  		p.From.Reg = v.Args[0].Reg()
   349  		p.Reg = v.Args[1].Reg()
   350  		p.To.Type = obj.TYPE_REG
   351  		p.To.Reg = loong64.REG_FCC0
   352  
   353  	case ssa.OpLOONG64FMADDF,
   354  		ssa.OpLOONG64FMADDD,
   355  		ssa.OpLOONG64FMSUBF,
   356  		ssa.OpLOONG64FMSUBD,
   357  		ssa.OpLOONG64FNMADDF,
   358  		ssa.OpLOONG64FNMADDD,
   359  		ssa.OpLOONG64FNMSUBF,
   360  		ssa.OpLOONG64FNMSUBD:
   361  		p := s.Prog(v.Op.Asm())
   362  		// r=(FMA x y z) -> FMADDD z, y, x, r
   363  		// the SSA operand order is for taking advantage of
   364  		// commutativity (that only applies for the first two operands)
   365  		r := v.Reg()
   366  		x := v.Args[0].Reg()
   367  		y := v.Args[1].Reg()
   368  		z := v.Args[2].Reg()
   369  		p.From.Type = obj.TYPE_REG
   370  		p.From.Reg = z
   371  		p.Reg = y
   372  		p.AddRestSourceReg(x)
   373  		p.To.Type = obj.TYPE_REG
   374  		p.To.Reg = r
   375  
   376  	case ssa.OpLOONG64MOVVaddr:
   377  		p := s.Prog(loong64.AMOVV)
   378  		p.From.Type = obj.TYPE_ADDR
   379  		p.From.Reg = v.Args[0].Reg()
   380  		var wantreg string
   381  		// MOVV $sym+off(base), R
   382  		// the assembler expands it as the following:
   383  		// - base is SP: add constant offset to SP (R3)
   384  		// when constant is large, tmp register (R30) may be used
   385  		// - base is SB: load external address with relocation
   386  		switch v.Aux.(type) {
   387  		default:
   388  			v.Fatalf("aux is of unknown type %T", v.Aux)
   389  		case *obj.LSym:
   390  			wantreg = "SB"
   391  			ssagen.AddAux(&p.From, v)
   392  		case *ir.Name:
   393  			wantreg = "SP"
   394  			ssagen.AddAux(&p.From, v)
   395  		case nil:
   396  			// No sym, just MOVV $off(SP), R
   397  			wantreg = "SP"
   398  			p.From.Offset = v.AuxInt
   399  		}
   400  		if reg := v.Args[0].RegName(); reg != wantreg {
   401  			v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg)
   402  		}
   403  		p.To.Type = obj.TYPE_REG
   404  		p.To.Reg = v.Reg()
   405  
   406  	case ssa.OpLOONG64MOVBloadidx,
   407  		ssa.OpLOONG64MOVBUloadidx,
   408  		ssa.OpLOONG64MOVHloadidx,
   409  		ssa.OpLOONG64MOVHUloadidx,
   410  		ssa.OpLOONG64MOVWloadidx,
   411  		ssa.OpLOONG64MOVWUloadidx,
   412  		ssa.OpLOONG64MOVVloadidx,
   413  		ssa.OpLOONG64MOVFloadidx,
   414  		ssa.OpLOONG64MOVDloadidx:
   415  		p := s.Prog(v.Op.Asm())
   416  		p.From.Type = obj.TYPE_MEM
   417  		p.From.Name = obj.NAME_NONE
   418  		p.From.Reg = v.Args[0].Reg()
   419  		p.From.Index = v.Args[1].Reg()
   420  		p.To.Type = obj.TYPE_REG
   421  		p.To.Reg = v.Reg()
   422  
   423  	case ssa.OpLOONG64MOVBstoreidx,
   424  		ssa.OpLOONG64MOVHstoreidx,
   425  		ssa.OpLOONG64MOVWstoreidx,
   426  		ssa.OpLOONG64MOVVstoreidx,
   427  		ssa.OpLOONG64MOVFstoreidx,
   428  		ssa.OpLOONG64MOVDstoreidx:
   429  		p := s.Prog(v.Op.Asm())
   430  		p.From.Type = obj.TYPE_REG
   431  		p.From.Reg = v.Args[2].Reg()
   432  		p.To.Type = obj.TYPE_MEM
   433  		p.To.Name = obj.NAME_NONE
   434  		p.To.Reg = v.Args[0].Reg()
   435  		p.To.Index = v.Args[1].Reg()
   436  
   437  	case ssa.OpLOONG64MOVBload,
   438  		ssa.OpLOONG64MOVBUload,
   439  		ssa.OpLOONG64MOVHload,
   440  		ssa.OpLOONG64MOVHUload,
   441  		ssa.OpLOONG64MOVWload,
   442  		ssa.OpLOONG64MOVWUload,
   443  		ssa.OpLOONG64MOVVload,
   444  		ssa.OpLOONG64MOVFload,
   445  		ssa.OpLOONG64MOVDload:
   446  		p := s.Prog(v.Op.Asm())
   447  		p.From.Type = obj.TYPE_MEM
   448  		p.From.Reg = v.Args[0].Reg()
   449  		ssagen.AddAux(&p.From, v)
   450  		p.To.Type = obj.TYPE_REG
   451  		p.To.Reg = v.Reg()
   452  	case ssa.OpLOONG64MOVBstore,
   453  		ssa.OpLOONG64MOVHstore,
   454  		ssa.OpLOONG64MOVWstore,
   455  		ssa.OpLOONG64MOVVstore,
   456  		ssa.OpLOONG64MOVFstore,
   457  		ssa.OpLOONG64MOVDstore:
   458  		p := s.Prog(v.Op.Asm())
   459  		p.From.Type = obj.TYPE_REG
   460  		p.From.Reg = v.Args[1].Reg()
   461  		p.To.Type = obj.TYPE_MEM
   462  		p.To.Reg = v.Args[0].Reg()
   463  		ssagen.AddAux(&p.To, v)
   464  	case ssa.OpLOONG64MOVBreg,
   465  		ssa.OpLOONG64MOVBUreg,
   466  		ssa.OpLOONG64MOVHreg,
   467  		ssa.OpLOONG64MOVHUreg,
   468  		ssa.OpLOONG64MOVWreg,
   469  		ssa.OpLOONG64MOVWUreg:
   470  		a := v.Args[0]
   471  		for a.Op == ssa.OpCopy || a.Op == ssa.OpLOONG64MOVVreg {
   472  			a = a.Args[0]
   473  		}
   474  		if a.Op == ssa.OpLoadReg && loong64.REG_R0 <= a.Reg() && a.Reg() <= loong64.REG_R31 {
   475  			// LoadReg from a narrower type does an extension, except loading
   476  			// to a floating point register. So only eliminate the extension
   477  			// if it is loaded to an integer register.
   478  
   479  			t := a.Type
   480  			switch {
   481  			case v.Op == ssa.OpLOONG64MOVBreg && t.Size() == 1 && t.IsSigned(),
   482  				v.Op == ssa.OpLOONG64MOVBUreg && t.Size() == 1 && !t.IsSigned(),
   483  				v.Op == ssa.OpLOONG64MOVHreg && t.Size() == 2 && t.IsSigned(),
   484  				v.Op == ssa.OpLOONG64MOVHUreg && t.Size() == 2 && !t.IsSigned(),
   485  				v.Op == ssa.OpLOONG64MOVWreg && t.Size() == 4 && t.IsSigned(),
   486  				v.Op == ssa.OpLOONG64MOVWUreg && t.Size() == 4 && !t.IsSigned():
   487  				// arg is a proper-typed load, already zero/sign-extended, don't extend again
   488  				if v.Reg() == v.Args[0].Reg() {
   489  					return
   490  				}
   491  				p := s.Prog(loong64.AMOVV)
   492  				p.From.Type = obj.TYPE_REG
   493  				p.From.Reg = v.Args[0].Reg()
   494  				p.To.Type = obj.TYPE_REG
   495  				p.To.Reg = v.Reg()
   496  				return
   497  			default:
   498  			}
   499  		}
   500  		fallthrough
   501  
   502  	case ssa.OpLOONG64MOVWF,
   503  		ssa.OpLOONG64MOVWD,
   504  		ssa.OpLOONG64TRUNCFW,
   505  		ssa.OpLOONG64TRUNCDW,
   506  		ssa.OpLOONG64MOVVF,
   507  		ssa.OpLOONG64MOVVD,
   508  		ssa.OpLOONG64TRUNCFV,
   509  		ssa.OpLOONG64TRUNCDV,
   510  		ssa.OpLOONG64MOVFD,
   511  		ssa.OpLOONG64MOVDF,
   512  		ssa.OpLOONG64MOVWfpgp,
   513  		ssa.OpLOONG64MOVWgpfp,
   514  		ssa.OpLOONG64MOVVfpgp,
   515  		ssa.OpLOONG64MOVVgpfp,
   516  		ssa.OpLOONG64NEGF,
   517  		ssa.OpLOONG64NEGD,
   518  		ssa.OpLOONG64CLZW,
   519  		ssa.OpLOONG64CLZV,
   520  		ssa.OpLOONG64CTZW,
   521  		ssa.OpLOONG64CTZV,
   522  		ssa.OpLOONG64SQRTD,
   523  		ssa.OpLOONG64SQRTF,
   524  		ssa.OpLOONG64REVB2H,
   525  		ssa.OpLOONG64REVB2W,
   526  		ssa.OpLOONG64REVB4H,
   527  		ssa.OpLOONG64REVBV,
   528  		ssa.OpLOONG64BITREV4B,
   529  		ssa.OpLOONG64BITREVW,
   530  		ssa.OpLOONG64BITREVV,
   531  		ssa.OpLOONG64ABSD:
   532  		p := s.Prog(v.Op.Asm())
   533  		p.From.Type = obj.TYPE_REG
   534  		p.From.Reg = v.Args[0].Reg()
   535  		p.To.Type = obj.TYPE_REG
   536  		p.To.Reg = v.Reg()
   537  
   538  	case ssa.OpLOONG64VPCNT64,
   539  		ssa.OpLOONG64VPCNT32,
   540  		ssa.OpLOONG64VPCNT16:
   541  		p := s.Prog(v.Op.Asm())
   542  		p.From.Type = obj.TYPE_REG
   543  		p.From.Reg = ((v.Args[0].Reg() - loong64.REG_F0) & 31) + loong64.REG_V0
   544  		p.To.Type = obj.TYPE_REG
   545  		p.To.Reg = ((v.Reg() - loong64.REG_F0) & 31) + loong64.REG_V0
   546  
   547  	case ssa.OpLOONG64NEGV:
   548  		// SUB from REGZERO
   549  		p := s.Prog(loong64.ASUBVU)
   550  		p.From.Type = obj.TYPE_REG
   551  		p.From.Reg = v.Args[0].Reg()
   552  		p.Reg = loong64.REGZERO
   553  		p.To.Type = obj.TYPE_REG
   554  		p.To.Reg = v.Reg()
   555  
   556  	case ssa.OpLOONG64LoweredZero:
   557  		ptrReg := v.Args[0].Reg()
   558  		n := v.AuxInt
   559  		if n < 16 {
   560  			v.Fatalf("Zero too small %d", n)
   561  		}
   562  
   563  		// Generate Zeroing instructions.
   564  		var off int64
   565  		for n >= 8 {
   566  			// MOVV     ZR, off(ptrReg)
   567  			zero8(s, ptrReg, off)
   568  			off += 8
   569  			n -= 8
   570  		}
   571  		if n != 0 {
   572  			// MOVV     ZR, off+n-8(ptrReg)
   573  			zero8(s, ptrReg, off+n-8)
   574  		}
   575  	case ssa.OpLOONG64LoweredZeroLoop:
   576  		ptrReg := v.Args[0].Reg()
   577  		countReg := v.RegTmp()
   578  		var off int64
   579  		n := v.AuxInt
   580  		loopSize := int64(64)
   581  		if n < 3*loopSize {
   582  			// - a loop count of 0 won't work.
   583  			// - a loop count of 1 is useless.
   584  			// - a loop count of 2 is a code size ~tie
   585  			//     4 instructions to implement the loop
   586  			//     8 instructions in the loop body
   587  			//   vs
   588  			//     16 instuctions in the straightline code
   589  			//   Might as well use straightline code.
   590  			v.Fatalf("ZeroLoop size tool small %d", n)
   591  		}
   592  
   593  		// Put iteration count in a register.
   594  		//   MOVV     $n/loopSize, countReg
   595  		p := s.Prog(loong64.AMOVV)
   596  		p.From.Type = obj.TYPE_CONST
   597  		p.From.Offset = n / loopSize
   598  		p.To.Type = obj.TYPE_REG
   599  		p.To.Reg = countReg
   600  		cntInit := p
   601  
   602  		// Zero loopSize bytes starting at ptrReg.
   603  		for range loopSize / 8 {
   604  			// MOVV     ZR, off(ptrReg)
   605  			zero8(s, ptrReg, off)
   606  			off += 8
   607  		}
   608  
   609  		// Increment ptrReg by loopSize.
   610  		//   ADDV     $loopSize, ptrReg
   611  		p = s.Prog(loong64.AADDV)
   612  		p.From.Type = obj.TYPE_CONST
   613  		p.From.Offset = loopSize
   614  		p.To.Type = obj.TYPE_REG
   615  		p.To.Reg = ptrReg
   616  
   617  		// Decrement loop count.
   618  		//   SUBV     $1, countReg
   619  		p = s.Prog(loong64.ASUBV)
   620  		p.From.Type = obj.TYPE_CONST
   621  		p.From.Offset = 1
   622  		p.To.Type = obj.TYPE_REG
   623  		p.To.Reg = countReg
   624  
   625  		// Jump to loop header if we're not done yet.
   626  		//   BNE     countReg, loop header
   627  		p = s.Prog(loong64.ABNE)
   628  		p.From.Type = obj.TYPE_REG
   629  		p.From.Reg = countReg
   630  		p.To.Type = obj.TYPE_BRANCH
   631  		p.To.SetTarget(cntInit.Link)
   632  
   633  		// Multiples of the loop size are now done.
   634  		n %= loopSize
   635  
   636  		off = 0
   637  		// Write any fractional portion.
   638  		for n >= 8 {
   639  			// MOVV     ZR, off(ptrReg)
   640  			zero8(s, ptrReg, off)
   641  			off += 8
   642  			n -= 8
   643  		}
   644  
   645  		if n != 0 {
   646  			zero8(s, ptrReg, off+n-8)
   647  		}
   648  
   649  	case ssa.OpLOONG64LoweredMove:
   650  		dstReg := v.Args[0].Reg()
   651  		srcReg := v.Args[1].Reg()
   652  		if dstReg == srcReg {
   653  			break
   654  		}
   655  		tmpReg := int16(loong64.REG_R20)
   656  		n := v.AuxInt
   657  		if n < 16 {
   658  			v.Fatalf("Move too small %d", n)
   659  		}
   660  
   661  		var off int64
   662  		for n >= 8 {
   663  			// MOVV     off(srcReg), tmpReg
   664  			// MOVV     tmpReg, off(dstReg)
   665  			move8(s, srcReg, dstReg, tmpReg, off)
   666  			off += 8
   667  			n -= 8
   668  		}
   669  
   670  		if n != 0 {
   671  			// MOVV     off+n-8(srcReg), tmpReg
   672  			// MOVV     tmpReg, off+n-8(srcReg)
   673  			move8(s, srcReg, dstReg, tmpReg, off+n-8)
   674  		}
   675  	case ssa.OpLOONG64LoweredMoveLoop:
   676  		dstReg := v.Args[0].Reg()
   677  		srcReg := v.Args[1].Reg()
   678  		if dstReg == srcReg {
   679  			break
   680  		}
   681  		countReg := int16(loong64.REG_R20)
   682  		tmpReg := int16(loong64.REG_R21)
   683  		var off int64
   684  		n := v.AuxInt
   685  		loopSize := int64(64)
   686  		if n < 3*loopSize {
   687  			// - a loop count of 0 won't work.
   688  			// - a loop count of 1 is useless.
   689  			// - a loop count of 2 is a code size ~tie
   690  			//     4 instructions to implement the loop
   691  			//     8 instructions in the loop body
   692  			//   vs
   693  			//     16 instructions in the straightline code
   694  			//   Might as well use straightline code.
   695  			v.Fatalf("ZeroLoop size too small %d", n)
   696  		}
   697  
   698  		// Put iteration count in a register.
   699  		//   MOVV     $n/loopSize, countReg
   700  		p := s.Prog(loong64.AMOVV)
   701  		p.From.Type = obj.TYPE_CONST
   702  		p.From.Offset = n / loopSize
   703  		p.To.Type = obj.TYPE_REG
   704  		p.To.Reg = countReg
   705  		cntInit := p
   706  
   707  		// Move loopSize bytes starting at srcReg to dstReg.
   708  		for range loopSize / 8 {
   709  			// MOVV     off(srcReg), tmpReg
   710  			// MOVV     tmpReg, off(dstReg)
   711  			move8(s, srcReg, dstReg, tmpReg, off)
   712  			off += 8
   713  		}
   714  
   715  		// Increment srcReg and destReg by loopSize.
   716  		//   ADDV     $loopSize, srcReg
   717  		p = s.Prog(loong64.AADDV)
   718  		p.From.Type = obj.TYPE_CONST
   719  		p.From.Offset = loopSize
   720  		p.To.Type = obj.TYPE_REG
   721  		p.To.Reg = srcReg
   722  		//   ADDV     $loopSize, dstReg
   723  		p = s.Prog(loong64.AADDV)
   724  		p.From.Type = obj.TYPE_CONST
   725  		p.From.Offset = loopSize
   726  		p.To.Type = obj.TYPE_REG
   727  		p.To.Reg = dstReg
   728  
   729  		// Decrement loop count.
   730  		//   SUBV     $1, countReg
   731  		p = s.Prog(loong64.ASUBV)
   732  		p.From.Type = obj.TYPE_CONST
   733  		p.From.Offset = 1
   734  		p.To.Type = obj.TYPE_REG
   735  		p.To.Reg = countReg
   736  
   737  		// Jump to loop header if we're not done yet.
   738  		//   BNE     countReg, loop header
   739  		p = s.Prog(loong64.ABNE)
   740  		p.From.Type = obj.TYPE_REG
   741  		p.From.Reg = countReg
   742  		p.To.Type = obj.TYPE_BRANCH
   743  		p.To.SetTarget(cntInit.Link)
   744  
   745  		// Multiples of the loop size are now done.
   746  		n %= loopSize
   747  
   748  		off = 0
   749  		// Copy any fractional portion.
   750  		for n >= 8 {
   751  			// MOVV     off(srcReg), tmpReg
   752  			// MOVV     tmpReg, off(dstReg)
   753  			move8(s, srcReg, dstReg, tmpReg, off)
   754  			off += 8
   755  			n -= 8
   756  		}
   757  
   758  		if n != 0 {
   759  			// MOVV     off+n-8(srcReg), tmpReg
   760  			// MOVV     tmpReg, off+n-8(srcReg)
   761  			move8(s, srcReg, dstReg, tmpReg, off+n-8)
   762  		}
   763  
   764  	case ssa.OpLOONG64CALLstatic, ssa.OpLOONG64CALLclosure, ssa.OpLOONG64CALLinter:
   765  		s.Call(v)
   766  	case ssa.OpLOONG64CALLtail:
   767  		s.TailCall(v)
   768  	case ssa.OpLOONG64LoweredWB:
   769  		p := s.Prog(obj.ACALL)
   770  		p.To.Type = obj.TYPE_MEM
   771  		p.To.Name = obj.NAME_EXTERN
   772  		// AuxInt encodes how many buffer entries we need.
   773  		p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1]
   774  
   775  	case ssa.OpLOONG64LoweredPubBarrier:
   776  		// DBAR 0x1A
   777  		p := s.Prog(v.Op.Asm())
   778  		p.From.Type = obj.TYPE_CONST
   779  		p.From.Offset = 0x1A
   780  
   781  	case ssa.OpLOONG64LoweredPanicBoundsRR, ssa.OpLOONG64LoweredPanicBoundsRC, ssa.OpLOONG64LoweredPanicBoundsCR, ssa.OpLOONG64LoweredPanicBoundsCC:
   782  		// Compute the constant we put in the PCData entry for this call.
   783  		code, signed := ssa.BoundsKind(v.AuxInt).Code()
   784  		xIsReg := false
   785  		yIsReg := false
   786  		xVal := 0
   787  		yVal := 0
   788  		switch v.Op {
   789  		case ssa.OpLOONG64LoweredPanicBoundsRR:
   790  			xIsReg = true
   791  			xVal = int(v.Args[0].Reg() - loong64.REG_R4)
   792  			yIsReg = true
   793  			yVal = int(v.Args[1].Reg() - loong64.REG_R4)
   794  		case ssa.OpLOONG64LoweredPanicBoundsRC:
   795  			xIsReg = true
   796  			xVal = int(v.Args[0].Reg() - loong64.REG_R4)
   797  			c := v.Aux.(ssa.PanicBoundsC).C
   798  			if c >= 0 && c <= abi.BoundsMaxConst {
   799  				yVal = int(c)
   800  			} else {
   801  				// Move constant to a register
   802  				yIsReg = true
   803  				if yVal == xVal {
   804  					yVal = 1
   805  				}
   806  				p := s.Prog(loong64.AMOVV)
   807  				p.From.Type = obj.TYPE_CONST
   808  				p.From.Offset = c
   809  				p.To.Type = obj.TYPE_REG
   810  				p.To.Reg = loong64.REG_R4 + int16(yVal)
   811  			}
   812  		case ssa.OpLOONG64LoweredPanicBoundsCR:
   813  			yIsReg = true
   814  			yVal = int(v.Args[0].Reg() - loong64.REG_R4)
   815  			c := v.Aux.(ssa.PanicBoundsC).C
   816  			if c >= 0 && c <= abi.BoundsMaxConst {
   817  				xVal = int(c)
   818  			} else {
   819  				// Move constant to a register
   820  				xIsReg = true
   821  				if xVal == yVal {
   822  					xVal = 1
   823  				}
   824  				p := s.Prog(loong64.AMOVV)
   825  				p.From.Type = obj.TYPE_CONST
   826  				p.From.Offset = c
   827  				p.To.Type = obj.TYPE_REG
   828  				p.To.Reg = loong64.REG_R4 + int16(xVal)
   829  			}
   830  		case ssa.OpLOONG64LoweredPanicBoundsCC:
   831  			c := v.Aux.(ssa.PanicBoundsCC).Cx
   832  			if c >= 0 && c <= abi.BoundsMaxConst {
   833  				xVal = int(c)
   834  			} else {
   835  				// Move constant to a register
   836  				xIsReg = true
   837  				p := s.Prog(loong64.AMOVV)
   838  				p.From.Type = obj.TYPE_CONST
   839  				p.From.Offset = c
   840  				p.To.Type = obj.TYPE_REG
   841  				p.To.Reg = loong64.REG_R4 + int16(xVal)
   842  			}
   843  			c = v.Aux.(ssa.PanicBoundsCC).Cy
   844  			if c >= 0 && c <= abi.BoundsMaxConst {
   845  				yVal = int(c)
   846  			} else {
   847  				// Move constant to a register
   848  				yIsReg = true
   849  				yVal = 1
   850  				p := s.Prog(loong64.AMOVV)
   851  				p.From.Type = obj.TYPE_CONST
   852  				p.From.Offset = c
   853  				p.To.Type = obj.TYPE_REG
   854  				p.To.Reg = loong64.REG_R4 + int16(yVal)
   855  			}
   856  		}
   857  		c := abi.BoundsEncode(code, signed, xIsReg, yIsReg, xVal, yVal)
   858  
   859  		p := s.Prog(obj.APCDATA)
   860  		p.From.SetConst(abi.PCDATA_PanicBounds)
   861  		p.To.SetConst(int64(c))
   862  		p = s.Prog(obj.ACALL)
   863  		p.To.Type = obj.TYPE_MEM
   864  		p.To.Name = obj.NAME_EXTERN
   865  		p.To.Sym = ir.Syms.PanicBounds
   866  
   867  	case ssa.OpLOONG64LoweredAtomicLoad8, ssa.OpLOONG64LoweredAtomicLoad32, ssa.OpLOONG64LoweredAtomicLoad64:
   868  		// MOVB	(Rarg0), Rout
   869  		// DBAR	0x14
   870  		as := loong64.AMOVV
   871  		switch v.Op {
   872  		case ssa.OpLOONG64LoweredAtomicLoad8:
   873  			as = loong64.AMOVB
   874  		case ssa.OpLOONG64LoweredAtomicLoad32:
   875  			as = loong64.AMOVW
   876  		}
   877  		p := s.Prog(as)
   878  		p.From.Type = obj.TYPE_MEM
   879  		p.From.Reg = v.Args[0].Reg()
   880  		p.To.Type = obj.TYPE_REG
   881  		p.To.Reg = v.Reg0()
   882  		p1 := s.Prog(loong64.ADBAR)
   883  		p1.From.Type = obj.TYPE_CONST
   884  		p1.From.Offset = 0x14
   885  
   886  	case ssa.OpLOONG64LoweredAtomicStore8,
   887  		ssa.OpLOONG64LoweredAtomicStore32,
   888  		ssa.OpLOONG64LoweredAtomicStore64:
   889  		// DBAR 0x12
   890  		// MOVx (Rarg1), Rout
   891  		// DBAR 0x18
   892  		movx := loong64.AMOVV
   893  		switch v.Op {
   894  		case ssa.OpLOONG64LoweredAtomicStore8:
   895  			movx = loong64.AMOVB
   896  		case ssa.OpLOONG64LoweredAtomicStore32:
   897  			movx = loong64.AMOVW
   898  		}
   899  		p := s.Prog(loong64.ADBAR)
   900  		p.From.Type = obj.TYPE_CONST
   901  		p.From.Offset = 0x12
   902  
   903  		p1 := s.Prog(movx)
   904  		p1.From.Type = obj.TYPE_REG
   905  		p1.From.Reg = v.Args[1].Reg()
   906  		p1.To.Type = obj.TYPE_MEM
   907  		p1.To.Reg = v.Args[0].Reg()
   908  
   909  		p2 := s.Prog(loong64.ADBAR)
   910  		p2.From.Type = obj.TYPE_CONST
   911  		p2.From.Offset = 0x18
   912  
   913  	case ssa.OpLOONG64LoweredAtomicStore8Variant,
   914  		ssa.OpLOONG64LoweredAtomicStore32Variant,
   915  		ssa.OpLOONG64LoweredAtomicStore64Variant:
   916  		//AMSWAPx  Rarg1, (Rarg0), Rout
   917  		amswapx := loong64.AAMSWAPDBV
   918  		switch v.Op {
   919  		case ssa.OpLOONG64LoweredAtomicStore32Variant:
   920  			amswapx = loong64.AAMSWAPDBW
   921  		case ssa.OpLOONG64LoweredAtomicStore8Variant:
   922  			amswapx = loong64.AAMSWAPDBB
   923  		}
   924  		p := s.Prog(amswapx)
   925  		p.From.Type = obj.TYPE_REG
   926  		p.From.Reg = v.Args[1].Reg()
   927  		p.To.Type = obj.TYPE_MEM
   928  		p.To.Reg = v.Args[0].Reg()
   929  		p.RegTo2 = loong64.REGZERO
   930  
   931  	case ssa.OpLOONG64LoweredAtomicExchange32, ssa.OpLOONG64LoweredAtomicExchange64:
   932  		// AMSWAPx	Rarg1, (Rarg0), Rout
   933  		amswapx := loong64.AAMSWAPDBV
   934  		if v.Op == ssa.OpLOONG64LoweredAtomicExchange32 {
   935  			amswapx = loong64.AAMSWAPDBW
   936  		}
   937  		p := s.Prog(amswapx)
   938  		p.From.Type = obj.TYPE_REG
   939  		p.From.Reg = v.Args[1].Reg()
   940  		p.To.Type = obj.TYPE_MEM
   941  		p.To.Reg = v.Args[0].Reg()
   942  		p.RegTo2 = v.Reg0()
   943  
   944  	case ssa.OpLOONG64LoweredAtomicExchange8Variant:
   945  		// AMSWAPDBB	Rarg1, (Rarg0), Rout
   946  		p := s.Prog(loong64.AAMSWAPDBB)
   947  		p.From.Type = obj.TYPE_REG
   948  		p.From.Reg = v.Args[1].Reg()
   949  		p.To.Type = obj.TYPE_MEM
   950  		p.To.Reg = v.Args[0].Reg()
   951  		p.RegTo2 = v.Reg0()
   952  
   953  	case ssa.OpLOONG64LoweredAtomicAdd32, ssa.OpLOONG64LoweredAtomicAdd64:
   954  		// AMADDx  Rarg1, (Rarg0), Rout
   955  		// ADDV    Rarg1, Rout, Rout
   956  		amaddx := loong64.AAMADDDBV
   957  		addx := loong64.AADDV
   958  		if v.Op == ssa.OpLOONG64LoweredAtomicAdd32 {
   959  			amaddx = loong64.AAMADDDBW
   960  		}
   961  		p := s.Prog(amaddx)
   962  		p.From.Type = obj.TYPE_REG
   963  		p.From.Reg = v.Args[1].Reg()
   964  		p.To.Type = obj.TYPE_MEM
   965  		p.To.Reg = v.Args[0].Reg()
   966  		p.RegTo2 = v.Reg0()
   967  
   968  		p1 := s.Prog(addx)
   969  		p1.From.Type = obj.TYPE_REG
   970  		p1.From.Reg = v.Args[1].Reg()
   971  		p1.Reg = v.Reg0()
   972  		p1.To.Type = obj.TYPE_REG
   973  		p1.To.Reg = v.Reg0()
   974  
   975  	case ssa.OpLOONG64LoweredAtomicCas32, ssa.OpLOONG64LoweredAtomicCas64:
   976  		// MOVV $0, Rout
   977  		// DBAR 0x14
   978  		// LL	(Rarg0), Rtmp
   979  		// BNE	Rtmp, Rarg1, 4(PC)
   980  		// MOVV Rarg2, Rout
   981  		// SC	Rout, (Rarg0)
   982  		// BEQ	Rout, -4(PC)
   983  		// DBAR 0x12
   984  		ll := loong64.ALLV
   985  		sc := loong64.ASCV
   986  		if v.Op == ssa.OpLOONG64LoweredAtomicCas32 {
   987  			ll = loong64.ALL
   988  			sc = loong64.ASC
   989  		}
   990  
   991  		p := s.Prog(loong64.AMOVV)
   992  		p.From.Type = obj.TYPE_REG
   993  		p.From.Reg = loong64.REGZERO
   994  		p.To.Type = obj.TYPE_REG
   995  		p.To.Reg = v.Reg0()
   996  
   997  		p1 := s.Prog(loong64.ADBAR)
   998  		p1.From.Type = obj.TYPE_CONST
   999  		p1.From.Offset = 0x14
  1000  
  1001  		p2 := s.Prog(ll)
  1002  		p2.From.Type = obj.TYPE_MEM
  1003  		p2.From.Reg = v.Args[0].Reg()
  1004  		p2.To.Type = obj.TYPE_REG
  1005  		p2.To.Reg = loong64.REGTMP
  1006  
  1007  		p3 := s.Prog(loong64.ABNE)
  1008  		p3.From.Type = obj.TYPE_REG
  1009  		p3.From.Reg = v.Args[1].Reg()
  1010  		p3.Reg = loong64.REGTMP
  1011  		p3.To.Type = obj.TYPE_BRANCH
  1012  
  1013  		p4 := s.Prog(loong64.AMOVV)
  1014  		p4.From.Type = obj.TYPE_REG
  1015  		p4.From.Reg = v.Args[2].Reg()
  1016  		p4.To.Type = obj.TYPE_REG
  1017  		p4.To.Reg = v.Reg0()
  1018  
  1019  		p5 := s.Prog(sc)
  1020  		p5.From.Type = obj.TYPE_REG
  1021  		p5.From.Reg = v.Reg0()
  1022  		p5.To.Type = obj.TYPE_MEM
  1023  		p5.To.Reg = v.Args[0].Reg()
  1024  
  1025  		p6 := s.Prog(loong64.ABEQ)
  1026  		p6.From.Type = obj.TYPE_REG
  1027  		p6.From.Reg = v.Reg0()
  1028  		p6.To.Type = obj.TYPE_BRANCH
  1029  		p6.To.SetTarget(p2)
  1030  
  1031  		p7 := s.Prog(loong64.ADBAR)
  1032  		p7.From.Type = obj.TYPE_CONST
  1033  		p7.From.Offset = 0x12
  1034  		p3.To.SetTarget(p7)
  1035  
  1036  	case ssa.OpLOONG64LoweredAtomicAnd32,
  1037  		ssa.OpLOONG64LoweredAtomicOr32:
  1038  		// AM{AND,OR}DBx  Rarg1, (Rarg0), RegZero
  1039  		p := s.Prog(v.Op.Asm())
  1040  		p.From.Type = obj.TYPE_REG
  1041  		p.From.Reg = v.Args[1].Reg()
  1042  		p.To.Type = obj.TYPE_MEM
  1043  		p.To.Reg = v.Args[0].Reg()
  1044  		p.RegTo2 = loong64.REGZERO
  1045  
  1046  	case ssa.OpLOONG64LoweredAtomicAnd32value,
  1047  		ssa.OpLOONG64LoweredAtomicAnd64value,
  1048  		ssa.OpLOONG64LoweredAtomicOr64value,
  1049  		ssa.OpLOONG64LoweredAtomicOr32value:
  1050  		// AM{AND,OR}DBx  Rarg1, (Rarg0), Rout
  1051  		p := s.Prog(v.Op.Asm())
  1052  		p.From.Type = obj.TYPE_REG
  1053  		p.From.Reg = v.Args[1].Reg()
  1054  		p.To.Type = obj.TYPE_MEM
  1055  		p.To.Reg = v.Args[0].Reg()
  1056  		p.RegTo2 = v.Reg0()
  1057  
  1058  	case ssa.OpLOONG64LoweredAtomicCas64Variant, ssa.OpLOONG64LoweredAtomicCas32Variant:
  1059  		// MOVV         $0, Rout
  1060  		// MOVV         Rarg1, Rtmp
  1061  		// AMCASDBx     Rarg2, (Rarg0), Rtmp
  1062  		// BNE          Rarg1, Rtmp, 2(PC)
  1063  		// MOVV         $1, Rout
  1064  		// NOP
  1065  
  1066  		amcasx := loong64.AAMCASDBV
  1067  		if v.Op == ssa.OpLOONG64LoweredAtomicCas32Variant {
  1068  			amcasx = loong64.AAMCASDBW
  1069  		}
  1070  
  1071  		p := s.Prog(loong64.AMOVV)
  1072  		p.From.Type = obj.TYPE_REG
  1073  		p.From.Reg = loong64.REGZERO
  1074  		p.To.Type = obj.TYPE_REG
  1075  		p.To.Reg = v.Reg0()
  1076  
  1077  		p1 := s.Prog(loong64.AMOVV)
  1078  		p1.From.Type = obj.TYPE_REG
  1079  		p1.From.Reg = v.Args[1].Reg()
  1080  		p1.To.Type = obj.TYPE_REG
  1081  		p1.To.Reg = loong64.REGTMP
  1082  
  1083  		p2 := s.Prog(amcasx)
  1084  		p2.From.Type = obj.TYPE_REG
  1085  		p2.From.Reg = v.Args[2].Reg()
  1086  		p2.To.Type = obj.TYPE_MEM
  1087  		p2.To.Reg = v.Args[0].Reg()
  1088  		p2.RegTo2 = loong64.REGTMP
  1089  
  1090  		p3 := s.Prog(loong64.ABNE)
  1091  		p3.From.Type = obj.TYPE_REG
  1092  		p3.From.Reg = v.Args[1].Reg()
  1093  		p3.Reg = loong64.REGTMP
  1094  		p3.To.Type = obj.TYPE_BRANCH
  1095  
  1096  		p4 := s.Prog(loong64.AMOVV)
  1097  		p4.From.Type = obj.TYPE_CONST
  1098  		p4.From.Offset = 0x1
  1099  		p4.To.Type = obj.TYPE_REG
  1100  		p4.To.Reg = v.Reg0()
  1101  
  1102  		p5 := s.Prog(obj.ANOP)
  1103  		p3.To.SetTarget(p5)
  1104  
  1105  	case ssa.OpLOONG64LoweredNilCheck:
  1106  		// Issue a load which will fault if arg is nil.
  1107  		p := s.Prog(loong64.AMOVB)
  1108  		p.From.Type = obj.TYPE_MEM
  1109  		p.From.Reg = v.Args[0].Reg()
  1110  		ssagen.AddAux(&p.From, v)
  1111  		p.To.Type = obj.TYPE_REG
  1112  		p.To.Reg = loong64.REGTMP
  1113  		if logopt.Enabled() {
  1114  			logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
  1115  		}
  1116  		if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
  1117  			base.WarnfAt(v.Pos, "generated nil check")
  1118  		}
  1119  	case ssa.OpLOONG64FPFlagTrue,
  1120  		ssa.OpLOONG64FPFlagFalse:
  1121  		// MOVV	$0, r
  1122  		// BFPF	2(PC)
  1123  		// MOVV	$1, r
  1124  		branch := loong64.ABFPF
  1125  		if v.Op == ssa.OpLOONG64FPFlagFalse {
  1126  			branch = loong64.ABFPT
  1127  		}
  1128  		p := s.Prog(loong64.AMOVV)
  1129  		p.From.Type = obj.TYPE_REG
  1130  		p.From.Reg = loong64.REGZERO
  1131  		p.To.Type = obj.TYPE_REG
  1132  		p.To.Reg = v.Reg()
  1133  		p2 := s.Prog(branch)
  1134  		p2.To.Type = obj.TYPE_BRANCH
  1135  		p3 := s.Prog(loong64.AMOVV)
  1136  		p3.From.Type = obj.TYPE_CONST
  1137  		p3.From.Offset = 1
  1138  		p3.To.Type = obj.TYPE_REG
  1139  		p3.To.Reg = v.Reg()
  1140  		p4 := s.Prog(obj.ANOP) // not a machine instruction, for branch to land
  1141  		p2.To.SetTarget(p4)
  1142  	case ssa.OpLOONG64LoweredGetClosurePtr:
  1143  		// Closure pointer is R22 (loong64.REGCTXT).
  1144  		ssagen.CheckLoweredGetClosurePtr(v)
  1145  	case ssa.OpLOONG64LoweredGetCallerSP:
  1146  		// caller's SP is FixedFrameSize below the address of the first arg
  1147  		p := s.Prog(loong64.AMOVV)
  1148  		p.From.Type = obj.TYPE_ADDR
  1149  		p.From.Offset = -base.Ctxt.Arch.FixedFrameSize
  1150  		p.From.Name = obj.NAME_PARAM
  1151  		p.To.Type = obj.TYPE_REG
  1152  		p.To.Reg = v.Reg()
  1153  	case ssa.OpLOONG64LoweredGetCallerPC:
  1154  		p := s.Prog(obj.AGETCALLERPC)
  1155  		p.To.Type = obj.TYPE_REG
  1156  		p.To.Reg = v.Reg()
  1157  	case ssa.OpLOONG64MASKEQZ, ssa.OpLOONG64MASKNEZ:
  1158  		p := s.Prog(v.Op.Asm())
  1159  		p.From.Type = obj.TYPE_REG
  1160  		p.From.Reg = v.Args[1].Reg()
  1161  		p.Reg = v.Args[0].Reg()
  1162  		p.To.Type = obj.TYPE_REG
  1163  		p.To.Reg = v.Reg()
  1164  
  1165  	case ssa.OpLOONG64PRELD:
  1166  		// PRELD (Rarg0), hint
  1167  		p := s.Prog(v.Op.Asm())
  1168  		p.From.Type = obj.TYPE_MEM
  1169  		p.From.Reg = v.Args[0].Reg()
  1170  		p.AddRestSourceConst(v.AuxInt & 0x1f)
  1171  
  1172  	case ssa.OpLOONG64PRELDX:
  1173  		// PRELDX (Rarg0), $n, $hint
  1174  		p := s.Prog(v.Op.Asm())
  1175  		p.From.Type = obj.TYPE_MEM
  1176  		p.From.Reg = v.Args[0].Reg()
  1177  		p.AddRestSourceArgs([]obj.Addr{
  1178  			{Type: obj.TYPE_CONST, Offset: int64((v.AuxInt >> 5) & 0x1fffffffff)},
  1179  			{Type: obj.TYPE_CONST, Offset: int64((v.AuxInt >> 0) & 0x1f)},
  1180  		})
  1181  
  1182  	case ssa.OpLOONG64ADDshiftLLV:
  1183  		// ADDshiftLLV Rarg0, Rarg1, $shift
  1184  		// ALSLV $shift, Rarg1, Rarg0, Rtmp
  1185  		p := s.Prog(v.Op.Asm())
  1186  		p.From.Type = obj.TYPE_CONST
  1187  		p.From.Offset = v.AuxInt
  1188  		p.Reg = v.Args[1].Reg()
  1189  		p.AddRestSourceReg(v.Args[0].Reg())
  1190  		p.To.Type = obj.TYPE_REG
  1191  		p.To.Reg = v.Reg()
  1192  
  1193  	case ssa.OpClobber, ssa.OpClobberReg:
  1194  		// TODO: implement for clobberdead experiment. Nop is ok for now.
  1195  	default:
  1196  		v.Fatalf("genValue not implemented: %s", v.LongString())
  1197  	}
  1198  }
  1199  
  1200  var blockJump = map[ssa.BlockKind]struct {
  1201  	asm, invasm obj.As
  1202  }{
  1203  	ssa.BlockLOONG64EQZ:  {loong64.ABEQ, loong64.ABNE},
  1204  	ssa.BlockLOONG64NEZ:  {loong64.ABNE, loong64.ABEQ},
  1205  	ssa.BlockLOONG64LTZ:  {loong64.ABLTZ, loong64.ABGEZ},
  1206  	ssa.BlockLOONG64GEZ:  {loong64.ABGEZ, loong64.ABLTZ},
  1207  	ssa.BlockLOONG64LEZ:  {loong64.ABLEZ, loong64.ABGTZ},
  1208  	ssa.BlockLOONG64GTZ:  {loong64.ABGTZ, loong64.ABLEZ},
  1209  	ssa.BlockLOONG64FPT:  {loong64.ABFPT, loong64.ABFPF},
  1210  	ssa.BlockLOONG64FPF:  {loong64.ABFPF, loong64.ABFPT},
  1211  	ssa.BlockLOONG64BEQ:  {loong64.ABEQ, loong64.ABNE},
  1212  	ssa.BlockLOONG64BNE:  {loong64.ABNE, loong64.ABEQ},
  1213  	ssa.BlockLOONG64BGE:  {loong64.ABGE, loong64.ABLT},
  1214  	ssa.BlockLOONG64BLT:  {loong64.ABLT, loong64.ABGE},
  1215  	ssa.BlockLOONG64BLTU: {loong64.ABLTU, loong64.ABGEU},
  1216  	ssa.BlockLOONG64BGEU: {loong64.ABGEU, loong64.ABLTU},
  1217  }
  1218  
  1219  func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
  1220  	switch b.Kind {
  1221  	case ssa.BlockPlain, ssa.BlockDefer:
  1222  		if b.Succs[0].Block() != next {
  1223  			p := s.Prog(obj.AJMP)
  1224  			p.To.Type = obj.TYPE_BRANCH
  1225  			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
  1226  		}
  1227  	case ssa.BlockExit, ssa.BlockRetJmp:
  1228  	case ssa.BlockRet:
  1229  		s.Prog(obj.ARET)
  1230  	case ssa.BlockLOONG64EQZ, ssa.BlockLOONG64NEZ,
  1231  		ssa.BlockLOONG64LTZ, ssa.BlockLOONG64GEZ,
  1232  		ssa.BlockLOONG64LEZ, ssa.BlockLOONG64GTZ,
  1233  		ssa.BlockLOONG64BEQ, ssa.BlockLOONG64BNE,
  1234  		ssa.BlockLOONG64BLT, ssa.BlockLOONG64BGE,
  1235  		ssa.BlockLOONG64BLTU, ssa.BlockLOONG64BGEU,
  1236  		ssa.BlockLOONG64FPT, ssa.BlockLOONG64FPF:
  1237  		jmp := blockJump[b.Kind]
  1238  		var p *obj.Prog
  1239  		switch next {
  1240  		case b.Succs[0].Block():
  1241  			p = s.Br(jmp.invasm, b.Succs[1].Block())
  1242  		case b.Succs[1].Block():
  1243  			p = s.Br(jmp.asm, b.Succs[0].Block())
  1244  		default:
  1245  			if b.Likely != ssa.BranchUnlikely {
  1246  				p = s.Br(jmp.asm, b.Succs[0].Block())
  1247  				s.Br(obj.AJMP, b.Succs[1].Block())
  1248  			} else {
  1249  				p = s.Br(jmp.invasm, b.Succs[1].Block())
  1250  				s.Br(obj.AJMP, b.Succs[0].Block())
  1251  			}
  1252  		}
  1253  		switch b.Kind {
  1254  		case ssa.BlockLOONG64BEQ, ssa.BlockLOONG64BNE,
  1255  			ssa.BlockLOONG64BGE, ssa.BlockLOONG64BLT,
  1256  			ssa.BlockLOONG64BGEU, ssa.BlockLOONG64BLTU:
  1257  			p.From.Type = obj.TYPE_REG
  1258  			p.From.Reg = b.Controls[0].Reg()
  1259  			p.Reg = b.Controls[1].Reg()
  1260  		case ssa.BlockLOONG64EQZ, ssa.BlockLOONG64NEZ,
  1261  			ssa.BlockLOONG64LTZ, ssa.BlockLOONG64GEZ,
  1262  			ssa.BlockLOONG64LEZ, ssa.BlockLOONG64GTZ,
  1263  			ssa.BlockLOONG64FPT, ssa.BlockLOONG64FPF:
  1264  			if !b.Controls[0].Type.IsFlags() {
  1265  				p.From.Type = obj.TYPE_REG
  1266  				p.From.Reg = b.Controls[0].Reg()
  1267  			}
  1268  		}
  1269  	default:
  1270  		b.Fatalf("branch not implemented: %s", b.LongString())
  1271  	}
  1272  }
  1273  
  1274  func loadRegResult(s *ssagen.State, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog {
  1275  	p := s.Prog(loadByType(t, reg))
  1276  	p.From.Type = obj.TYPE_MEM
  1277  	p.From.Name = obj.NAME_AUTO
  1278  	p.From.Sym = n.Linksym()
  1279  	p.From.Offset = n.FrameOffset() + off
  1280  	p.To.Type = obj.TYPE_REG
  1281  	p.To.Reg = reg
  1282  	return p
  1283  }
  1284  
  1285  func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog {
  1286  	p = pp.Append(p, storeByType(t, reg), obj.TYPE_REG, reg, 0, obj.TYPE_MEM, 0, n.FrameOffset()+off)
  1287  	p.To.Name = obj.NAME_PARAM
  1288  	p.To.Sym = n.Linksym()
  1289  	p.Pos = p.Pos.WithNotStmt()
  1290  	return p
  1291  }
  1292  
  1293  // move8 copies 8 bytes at src+off to dst+off.
  1294  func move8(s *ssagen.State, src, dst, tmp int16, off int64) {
  1295  	// MOVV     off(src), tmp
  1296  	ld := s.Prog(loong64.AMOVV)
  1297  	ld.From.Type = obj.TYPE_MEM
  1298  	ld.From.Reg = src
  1299  	ld.From.Offset = off
  1300  	ld.To.Type = obj.TYPE_REG
  1301  	ld.To.Reg = tmp
  1302  	// MOVV     tmp, off(dst)
  1303  	st := s.Prog(loong64.AMOVV)
  1304  	st.From.Type = obj.TYPE_REG
  1305  	st.From.Reg = tmp
  1306  	st.To.Type = obj.TYPE_MEM
  1307  	st.To.Reg = dst
  1308  	st.To.Offset = off
  1309  }
  1310  
  1311  // zero8 zeroes 8 bytes at reg+off.
  1312  func zero8(s *ssagen.State, reg int16, off int64) {
  1313  	// MOVV     ZR, off(reg)
  1314  	p := s.Prog(loong64.AMOVV)
  1315  	p.From.Type = obj.TYPE_REG
  1316  	p.From.Reg = loong64.REGZERO
  1317  	p.To.Type = obj.TYPE_MEM
  1318  	p.To.Reg = reg
  1319  	p.To.Offset = off
  1320  }
  1321  

View as plain text