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

View as plain text