github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/loong64/ssa.go (about)

     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  	"github.com/go-asm/go/cmd/compile/base"
    11  	"github.com/go-asm/go/cmd/compile/ir"
    12  	"github.com/go-asm/go/cmd/compile/logopt"
    13  	"github.com/go-asm/go/cmd/compile/objw"
    14  	"github.com/go-asm/go/cmd/compile/ssa"
    15  	"github.com/go-asm/go/cmd/compile/ssagen"
    16  	"github.com/go-asm/go/cmd/compile/types"
    17  	"github.com/go-asm/go/cmd/obj"
    18  	"github.com/go-asm/go/cmd/obj/loong64"
    19  )
    20  
    21  // isFPreg reports whether r is an FP register.
    22  func isFPreg(r int16) bool {
    23  	return loong64.REG_F0 <= r && r <= loong64.REG_F31
    24  }
    25  
    26  // loadByType returns the load instruction of the given type.
    27  func loadByType(t *types.Type, r int16) obj.As {
    28  	if isFPreg(r) {
    29  		if t.Size() == 4 {
    30  			return loong64.AMOVF
    31  		} else {
    32  			return loong64.AMOVD
    33  		}
    34  	} else {
    35  		switch t.Size() {
    36  		case 1:
    37  			if t.IsSigned() {
    38  				return loong64.AMOVB
    39  			} else {
    40  				return loong64.AMOVBU
    41  			}
    42  		case 2:
    43  			if t.IsSigned() {
    44  				return loong64.AMOVH
    45  			} else {
    46  				return loong64.AMOVHU
    47  			}
    48  		case 4:
    49  			if t.IsSigned() {
    50  				return loong64.AMOVW
    51  			} else {
    52  				return loong64.AMOVWU
    53  			}
    54  		case 8:
    55  			return loong64.AMOVV
    56  		}
    57  	}
    58  	panic("bad load type")
    59  }
    60  
    61  // storeByType returns the store instruction of the given type.
    62  func storeByType(t *types.Type, r int16) obj.As {
    63  	if isFPreg(r) {
    64  		if t.Size() == 4 {
    65  			return loong64.AMOVF
    66  		} else {
    67  			return loong64.AMOVD
    68  		}
    69  	} else {
    70  		switch t.Size() {
    71  		case 1:
    72  			return loong64.AMOVB
    73  		case 2:
    74  			return loong64.AMOVH
    75  		case 4:
    76  			return loong64.AMOVW
    77  		case 8:
    78  			return loong64.AMOVV
    79  		}
    80  	}
    81  	panic("bad store type")
    82  }
    83  
    84  // largestMove returns the largest move instruction possible and its size,
    85  // given the alignment of the total size of the move.
    86  //
    87  // e.g., a 16-byte move may use MOVV, but an 11-byte move must use MOVB.
    88  //
    89  // Note that the moves may not be on naturally aligned addresses depending on
    90  // the source and destination.
    91  //
    92  // This matches the calculation in ssa.moveSize.
    93  func largestMove(alignment int64) (obj.As, int64) {
    94  	switch {
    95  	case alignment%8 == 0:
    96  		return loong64.AMOVV, 8
    97  	case alignment%4 == 0:
    98  		return loong64.AMOVW, 4
    99  	case alignment%2 == 0:
   100  		return loong64.AMOVH, 2
   101  	default:
   102  		return loong64.AMOVB, 1
   103  	}
   104  }
   105  
   106  func ssaGenValue(s *ssagen.State, v *ssa.Value) {
   107  	switch v.Op {
   108  	case ssa.OpCopy, ssa.OpLOONG64MOVVreg:
   109  		if v.Type.IsMemory() {
   110  			return
   111  		}
   112  		x := v.Args[0].Reg()
   113  		y := v.Reg()
   114  		if x == y {
   115  			return
   116  		}
   117  		as := loong64.AMOVV
   118  		if isFPreg(x) && isFPreg(y) {
   119  			as = loong64.AMOVD
   120  		}
   121  		p := s.Prog(as)
   122  		p.From.Type = obj.TYPE_REG
   123  		p.From.Reg = x
   124  		p.To.Type = obj.TYPE_REG
   125  		p.To.Reg = y
   126  	case ssa.OpLOONG64MOVVnop:
   127  		// nothing to do
   128  	case ssa.OpLoadReg:
   129  		if v.Type.IsFlags() {
   130  			v.Fatalf("load flags not implemented: %v", v.LongString())
   131  			return
   132  		}
   133  		r := v.Reg()
   134  		p := s.Prog(loadByType(v.Type, r))
   135  		ssagen.AddrAuto(&p.From, v.Args[0])
   136  		p.To.Type = obj.TYPE_REG
   137  		p.To.Reg = r
   138  	case ssa.OpStoreReg:
   139  		if v.Type.IsFlags() {
   140  			v.Fatalf("store flags not implemented: %v", v.LongString())
   141  			return
   142  		}
   143  		r := v.Args[0].Reg()
   144  		p := s.Prog(storeByType(v.Type, r))
   145  		p.From.Type = obj.TYPE_REG
   146  		p.From.Reg = r
   147  		ssagen.AddrAuto(&p.To, v)
   148  	case ssa.OpArgIntReg, ssa.OpArgFloatReg:
   149  		// The assembler needs to wrap the entry safepoint/stack growth code with spill/unspill
   150  		// The loop only runs once.
   151  		for _, a := range v.Block.Func.RegArgs {
   152  			// Pass the spill/unspill information along to the assembler, offset by size of
   153  			// the saved LR slot.
   154  			addr := ssagen.SpillSlotAddr(a, loong64.REGSP, base.Ctxt.Arch.FixedFrameSize)
   155  			s.FuncInfo().AddSpill(
   156  				obj.RegSpill{Reg: a.Reg, Addr: addr, Unspill: loadByType(a.Type, a.Reg), Spill: storeByType(a.Type, a.Reg)})
   157  		}
   158  		v.Block.Func.RegArgs = nil
   159  		ssagen.CheckArgReg(v)
   160  	case ssa.OpLOONG64ADDV,
   161  		ssa.OpLOONG64SUBV,
   162  		ssa.OpLOONG64AND,
   163  		ssa.OpLOONG64OR,
   164  		ssa.OpLOONG64XOR,
   165  		ssa.OpLOONG64NOR,
   166  		ssa.OpLOONG64SLLV,
   167  		ssa.OpLOONG64SRLV,
   168  		ssa.OpLOONG64SRAV,
   169  		ssa.OpLOONG64ROTR,
   170  		ssa.OpLOONG64ROTRV,
   171  		ssa.OpLOONG64ADDF,
   172  		ssa.OpLOONG64ADDD,
   173  		ssa.OpLOONG64SUBF,
   174  		ssa.OpLOONG64SUBD,
   175  		ssa.OpLOONG64MULF,
   176  		ssa.OpLOONG64MULD,
   177  		ssa.OpLOONG64DIVF,
   178  		ssa.OpLOONG64DIVD,
   179  		ssa.OpLOONG64MULV, ssa.OpLOONG64MULHV, ssa.OpLOONG64MULHVU,
   180  		ssa.OpLOONG64DIVV, ssa.OpLOONG64REMV, ssa.OpLOONG64DIVVU, ssa.OpLOONG64REMVU:
   181  		p := s.Prog(v.Op.Asm())
   182  		p.From.Type = obj.TYPE_REG
   183  		p.From.Reg = v.Args[1].Reg()
   184  		p.Reg = v.Args[0].Reg()
   185  		p.To.Type = obj.TYPE_REG
   186  		p.To.Reg = v.Reg()
   187  	case ssa.OpLOONG64SGT,
   188  		ssa.OpLOONG64SGTU:
   189  		p := s.Prog(v.Op.Asm())
   190  		p.From.Type = obj.TYPE_REG
   191  		p.From.Reg = v.Args[0].Reg()
   192  		p.Reg = v.Args[1].Reg()
   193  		p.To.Type = obj.TYPE_REG
   194  		p.To.Reg = v.Reg()
   195  	case ssa.OpLOONG64ADDVconst,
   196  		ssa.OpLOONG64SUBVconst,
   197  		ssa.OpLOONG64ANDconst,
   198  		ssa.OpLOONG64ORconst,
   199  		ssa.OpLOONG64XORconst,
   200  		ssa.OpLOONG64NORconst,
   201  		ssa.OpLOONG64SLLVconst,
   202  		ssa.OpLOONG64SRLVconst,
   203  		ssa.OpLOONG64SRAVconst,
   204  		ssa.OpLOONG64ROTRconst,
   205  		ssa.OpLOONG64ROTRVconst,
   206  		ssa.OpLOONG64SGTconst,
   207  		ssa.OpLOONG64SGTUconst:
   208  		p := s.Prog(v.Op.Asm())
   209  		p.From.Type = obj.TYPE_CONST
   210  		p.From.Offset = v.AuxInt
   211  		p.Reg = v.Args[0].Reg()
   212  		p.To.Type = obj.TYPE_REG
   213  		p.To.Reg = v.Reg()
   214  	case ssa.OpLOONG64MOVVconst:
   215  		r := v.Reg()
   216  		p := s.Prog(v.Op.Asm())
   217  		p.From.Type = obj.TYPE_CONST
   218  		p.From.Offset = v.AuxInt
   219  		p.To.Type = obj.TYPE_REG
   220  		p.To.Reg = r
   221  		if isFPreg(r) {
   222  			// cannot move into FP or special registers, use TMP as intermediate
   223  			p.To.Reg = loong64.REGTMP
   224  			p = s.Prog(loong64.AMOVV)
   225  			p.From.Type = obj.TYPE_REG
   226  			p.From.Reg = loong64.REGTMP
   227  			p.To.Type = obj.TYPE_REG
   228  			p.To.Reg = r
   229  		}
   230  	case ssa.OpLOONG64MOVFconst,
   231  		ssa.OpLOONG64MOVDconst:
   232  		p := s.Prog(v.Op.Asm())
   233  		p.From.Type = obj.TYPE_FCONST
   234  		p.From.Val = math.Float64frombits(uint64(v.AuxInt))
   235  		p.To.Type = obj.TYPE_REG
   236  		p.To.Reg = v.Reg()
   237  	case ssa.OpLOONG64CMPEQF,
   238  		ssa.OpLOONG64CMPEQD,
   239  		ssa.OpLOONG64CMPGEF,
   240  		ssa.OpLOONG64CMPGED,
   241  		ssa.OpLOONG64CMPGTF,
   242  		ssa.OpLOONG64CMPGTD:
   243  		p := s.Prog(v.Op.Asm())
   244  		p.From.Type = obj.TYPE_REG
   245  		p.From.Reg = v.Args[0].Reg()
   246  		p.Reg = v.Args[1].Reg()
   247  	case ssa.OpLOONG64MOVVaddr:
   248  		p := s.Prog(loong64.AMOVV)
   249  		p.From.Type = obj.TYPE_ADDR
   250  		p.From.Reg = v.Args[0].Reg()
   251  		var wantreg string
   252  		// MOVV $sym+off(base), R
   253  		// the assembler expands it as the following:
   254  		// - base is SP: add constant offset to SP (R3)
   255  		// when constant is large, tmp register (R30) may be used
   256  		// - base is SB: load external address with relocation
   257  		switch v.Aux.(type) {
   258  		default:
   259  			v.Fatalf("aux is of unknown type %T", v.Aux)
   260  		case *obj.LSym:
   261  			wantreg = "SB"
   262  			ssagen.AddAux(&p.From, v)
   263  		case *ir.Name:
   264  			wantreg = "SP"
   265  			ssagen.AddAux(&p.From, v)
   266  		case nil:
   267  			// No sym, just MOVV $off(SP), R
   268  			wantreg = "SP"
   269  			p.From.Offset = v.AuxInt
   270  		}
   271  		if reg := v.Args[0].RegName(); reg != wantreg {
   272  			v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg)
   273  		}
   274  		p.To.Type = obj.TYPE_REG
   275  		p.To.Reg = v.Reg()
   276  	case ssa.OpLOONG64MOVBload,
   277  		ssa.OpLOONG64MOVBUload,
   278  		ssa.OpLOONG64MOVHload,
   279  		ssa.OpLOONG64MOVHUload,
   280  		ssa.OpLOONG64MOVWload,
   281  		ssa.OpLOONG64MOVWUload,
   282  		ssa.OpLOONG64MOVVload,
   283  		ssa.OpLOONG64MOVFload,
   284  		ssa.OpLOONG64MOVDload:
   285  		p := s.Prog(v.Op.Asm())
   286  		p.From.Type = obj.TYPE_MEM
   287  		p.From.Reg = v.Args[0].Reg()
   288  		ssagen.AddAux(&p.From, v)
   289  		p.To.Type = obj.TYPE_REG
   290  		p.To.Reg = v.Reg()
   291  	case ssa.OpLOONG64MOVBstore,
   292  		ssa.OpLOONG64MOVHstore,
   293  		ssa.OpLOONG64MOVWstore,
   294  		ssa.OpLOONG64MOVVstore,
   295  		ssa.OpLOONG64MOVFstore,
   296  		ssa.OpLOONG64MOVDstore:
   297  		p := s.Prog(v.Op.Asm())
   298  		p.From.Type = obj.TYPE_REG
   299  		p.From.Reg = v.Args[1].Reg()
   300  		p.To.Type = obj.TYPE_MEM
   301  		p.To.Reg = v.Args[0].Reg()
   302  		ssagen.AddAux(&p.To, v)
   303  	case ssa.OpLOONG64MOVBstorezero,
   304  		ssa.OpLOONG64MOVHstorezero,
   305  		ssa.OpLOONG64MOVWstorezero,
   306  		ssa.OpLOONG64MOVVstorezero:
   307  		p := s.Prog(v.Op.Asm())
   308  		p.From.Type = obj.TYPE_REG
   309  		p.From.Reg = loong64.REGZERO
   310  		p.To.Type = obj.TYPE_MEM
   311  		p.To.Reg = v.Args[0].Reg()
   312  		ssagen.AddAux(&p.To, v)
   313  	case ssa.OpLOONG64MOVBreg,
   314  		ssa.OpLOONG64MOVBUreg,
   315  		ssa.OpLOONG64MOVHreg,
   316  		ssa.OpLOONG64MOVHUreg,
   317  		ssa.OpLOONG64MOVWreg,
   318  		ssa.OpLOONG64MOVWUreg:
   319  		a := v.Args[0]
   320  		for a.Op == ssa.OpCopy || a.Op == ssa.OpLOONG64MOVVreg {
   321  			a = a.Args[0]
   322  		}
   323  		if a.Op == ssa.OpLoadReg && loong64.REG_R0 <= a.Reg() && a.Reg() <= loong64.REG_R31 {
   324  			// LoadReg from a narrower type does an extension, except loading
   325  			// to a floating point register. So only eliminate the extension
   326  			// if it is loaded to an integer register.
   327  
   328  			t := a.Type
   329  			switch {
   330  			case v.Op == ssa.OpLOONG64MOVBreg && t.Size() == 1 && t.IsSigned(),
   331  				v.Op == ssa.OpLOONG64MOVBUreg && t.Size() == 1 && !t.IsSigned(),
   332  				v.Op == ssa.OpLOONG64MOVHreg && t.Size() == 2 && t.IsSigned(),
   333  				v.Op == ssa.OpLOONG64MOVHUreg && t.Size() == 2 && !t.IsSigned(),
   334  				v.Op == ssa.OpLOONG64MOVWreg && t.Size() == 4 && t.IsSigned(),
   335  				v.Op == ssa.OpLOONG64MOVWUreg && t.Size() == 4 && !t.IsSigned():
   336  				// arg is a proper-typed load, already zero/sign-extended, don't extend again
   337  				if v.Reg() == v.Args[0].Reg() {
   338  					return
   339  				}
   340  				p := s.Prog(loong64.AMOVV)
   341  				p.From.Type = obj.TYPE_REG
   342  				p.From.Reg = v.Args[0].Reg()
   343  				p.To.Type = obj.TYPE_REG
   344  				p.To.Reg = v.Reg()
   345  				return
   346  			default:
   347  			}
   348  		}
   349  		fallthrough
   350  	case ssa.OpLOONG64MOVWF,
   351  		ssa.OpLOONG64MOVWD,
   352  		ssa.OpLOONG64TRUNCFW,
   353  		ssa.OpLOONG64TRUNCDW,
   354  		ssa.OpLOONG64MOVVF,
   355  		ssa.OpLOONG64MOVVD,
   356  		ssa.OpLOONG64TRUNCFV,
   357  		ssa.OpLOONG64TRUNCDV,
   358  		ssa.OpLOONG64MOVFD,
   359  		ssa.OpLOONG64MOVDF,
   360  		ssa.OpLOONG64NEGF,
   361  		ssa.OpLOONG64NEGD,
   362  		ssa.OpLOONG64SQRTD,
   363  		ssa.OpLOONG64SQRTF:
   364  		p := s.Prog(v.Op.Asm())
   365  		p.From.Type = obj.TYPE_REG
   366  		p.From.Reg = v.Args[0].Reg()
   367  		p.To.Type = obj.TYPE_REG
   368  		p.To.Reg = v.Reg()
   369  	case ssa.OpLOONG64NEGV:
   370  		// SUB from REGZERO
   371  		p := s.Prog(loong64.ASUBVU)
   372  		p.From.Type = obj.TYPE_REG
   373  		p.From.Reg = v.Args[0].Reg()
   374  		p.Reg = loong64.REGZERO
   375  		p.To.Type = obj.TYPE_REG
   376  		p.To.Reg = v.Reg()
   377  	case ssa.OpLOONG64DUFFZERO:
   378  		// runtime.duffzero expects start address in R20
   379  		p := s.Prog(obj.ADUFFZERO)
   380  		p.To.Type = obj.TYPE_MEM
   381  		p.To.Name = obj.NAME_EXTERN
   382  		p.To.Sym = ir.Syms.Duffzero
   383  		p.To.Offset = v.AuxInt
   384  	case ssa.OpLOONG64LoweredZero:
   385  		// MOVx	R0, (Rarg0)
   386  		// ADDV	$sz, Rarg0
   387  		// BGEU	Rarg1, Rarg0, -2(PC)
   388  		mov, sz := largestMove(v.AuxInt)
   389  		p := s.Prog(mov)
   390  		p.From.Type = obj.TYPE_REG
   391  		p.From.Reg = loong64.REGZERO
   392  		p.To.Type = obj.TYPE_MEM
   393  		p.To.Reg = v.Args[0].Reg()
   394  
   395  		p2 := s.Prog(loong64.AADDVU)
   396  		p2.From.Type = obj.TYPE_CONST
   397  		p2.From.Offset = sz
   398  		p2.To.Type = obj.TYPE_REG
   399  		p2.To.Reg = v.Args[0].Reg()
   400  
   401  		p3 := s.Prog(loong64.ABGEU)
   402  		p3.From.Type = obj.TYPE_REG
   403  		p3.From.Reg = v.Args[1].Reg()
   404  		p3.Reg = v.Args[0].Reg()
   405  		p3.To.Type = obj.TYPE_BRANCH
   406  		p3.To.SetTarget(p)
   407  
   408  	case ssa.OpLOONG64DUFFCOPY:
   409  		p := s.Prog(obj.ADUFFCOPY)
   410  		p.To.Type = obj.TYPE_MEM
   411  		p.To.Name = obj.NAME_EXTERN
   412  		p.To.Sym = ir.Syms.Duffcopy
   413  		p.To.Offset = v.AuxInt
   414  	case ssa.OpLOONG64LoweredMove:
   415  		// MOVx	(Rarg1), Rtmp
   416  		// MOVx	Rtmp, (Rarg0)
   417  		// ADDV	$sz, Rarg1
   418  		// ADDV	$sz, Rarg0
   419  		// BGEU	Rarg2, Rarg0, -4(PC)
   420  		mov, sz := largestMove(v.AuxInt)
   421  		p := s.Prog(mov)
   422  		p.From.Type = obj.TYPE_MEM
   423  		p.From.Reg = v.Args[1].Reg()
   424  		p.To.Type = obj.TYPE_REG
   425  		p.To.Reg = loong64.REGTMP
   426  
   427  		p2 := s.Prog(mov)
   428  		p2.From.Type = obj.TYPE_REG
   429  		p2.From.Reg = loong64.REGTMP
   430  		p2.To.Type = obj.TYPE_MEM
   431  		p2.To.Reg = v.Args[0].Reg()
   432  
   433  		p3 := s.Prog(loong64.AADDVU)
   434  		p3.From.Type = obj.TYPE_CONST
   435  		p3.From.Offset = sz
   436  		p3.To.Type = obj.TYPE_REG
   437  		p3.To.Reg = v.Args[1].Reg()
   438  
   439  		p4 := s.Prog(loong64.AADDVU)
   440  		p4.From.Type = obj.TYPE_CONST
   441  		p4.From.Offset = sz
   442  		p4.To.Type = obj.TYPE_REG
   443  		p4.To.Reg = v.Args[0].Reg()
   444  
   445  		p5 := s.Prog(loong64.ABGEU)
   446  		p5.From.Type = obj.TYPE_REG
   447  		p5.From.Reg = v.Args[2].Reg()
   448  		p5.Reg = v.Args[1].Reg()
   449  		p5.To.Type = obj.TYPE_BRANCH
   450  		p5.To.SetTarget(p)
   451  
   452  	case ssa.OpLOONG64CALLstatic, ssa.OpLOONG64CALLclosure, ssa.OpLOONG64CALLinter:
   453  		s.Call(v)
   454  	case ssa.OpLOONG64CALLtail:
   455  		s.TailCall(v)
   456  	case ssa.OpLOONG64LoweredWB:
   457  		p := s.Prog(obj.ACALL)
   458  		p.To.Type = obj.TYPE_MEM
   459  		p.To.Name = obj.NAME_EXTERN
   460  		// AuxInt encodes how many buffer entries we need.
   461  		p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1]
   462  	case ssa.OpLOONG64LoweredPanicBoundsA, ssa.OpLOONG64LoweredPanicBoundsB, ssa.OpLOONG64LoweredPanicBoundsC:
   463  		p := s.Prog(obj.ACALL)
   464  		p.To.Type = obj.TYPE_MEM
   465  		p.To.Name = obj.NAME_EXTERN
   466  		p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt]
   467  		s.UseArgs(16) // space used in callee args area by assembly stubs
   468  	case ssa.OpLOONG64LoweredAtomicLoad8, ssa.OpLOONG64LoweredAtomicLoad32, ssa.OpLOONG64LoweredAtomicLoad64:
   469  		as := loong64.AMOVV
   470  		switch v.Op {
   471  		case ssa.OpLOONG64LoweredAtomicLoad8:
   472  			as = loong64.AMOVB
   473  		case ssa.OpLOONG64LoweredAtomicLoad32:
   474  			as = loong64.AMOVW
   475  		}
   476  		s.Prog(loong64.ADBAR)
   477  		p := s.Prog(as)
   478  		p.From.Type = obj.TYPE_MEM
   479  		p.From.Reg = v.Args[0].Reg()
   480  		p.To.Type = obj.TYPE_REG
   481  		p.To.Reg = v.Reg0()
   482  		s.Prog(loong64.ADBAR)
   483  	case ssa.OpLOONG64LoweredAtomicStore8, ssa.OpLOONG64LoweredAtomicStore32, ssa.OpLOONG64LoweredAtomicStore64:
   484  		as := loong64.AMOVV
   485  		switch v.Op {
   486  		case ssa.OpLOONG64LoweredAtomicStore8:
   487  			as = loong64.AMOVB
   488  		case ssa.OpLOONG64LoweredAtomicStore32:
   489  			as = loong64.AMOVW
   490  		}
   491  		s.Prog(loong64.ADBAR)
   492  		p := s.Prog(as)
   493  		p.From.Type = obj.TYPE_REG
   494  		p.From.Reg = v.Args[1].Reg()
   495  		p.To.Type = obj.TYPE_MEM
   496  		p.To.Reg = v.Args[0].Reg()
   497  		s.Prog(loong64.ADBAR)
   498  	case ssa.OpLOONG64LoweredAtomicStorezero32, ssa.OpLOONG64LoweredAtomicStorezero64:
   499  		as := loong64.AMOVV
   500  		if v.Op == ssa.OpLOONG64LoweredAtomicStorezero32 {
   501  			as = loong64.AMOVW
   502  		}
   503  		s.Prog(loong64.ADBAR)
   504  		p := s.Prog(as)
   505  		p.From.Type = obj.TYPE_REG
   506  		p.From.Reg = loong64.REGZERO
   507  		p.To.Type = obj.TYPE_MEM
   508  		p.To.Reg = v.Args[0].Reg()
   509  		s.Prog(loong64.ADBAR)
   510  	case ssa.OpLOONG64LoweredAtomicExchange32, ssa.OpLOONG64LoweredAtomicExchange64:
   511  		// DBAR
   512  		// MOVV	Rarg1, Rtmp
   513  		// LL	(Rarg0), Rout
   514  		// SC	Rtmp, (Rarg0)
   515  		// BEQ	Rtmp, -3(PC)
   516  		// DBAR
   517  		ll := loong64.ALLV
   518  		sc := loong64.ASCV
   519  		if v.Op == ssa.OpLOONG64LoweredAtomicExchange32 {
   520  			ll = loong64.ALL
   521  			sc = loong64.ASC
   522  		}
   523  		s.Prog(loong64.ADBAR)
   524  		p := s.Prog(loong64.AMOVV)
   525  		p.From.Type = obj.TYPE_REG
   526  		p.From.Reg = v.Args[1].Reg()
   527  		p.To.Type = obj.TYPE_REG
   528  		p.To.Reg = loong64.REGTMP
   529  		p1 := s.Prog(ll)
   530  		p1.From.Type = obj.TYPE_MEM
   531  		p1.From.Reg = v.Args[0].Reg()
   532  		p1.To.Type = obj.TYPE_REG
   533  		p1.To.Reg = v.Reg0()
   534  		p2 := s.Prog(sc)
   535  		p2.From.Type = obj.TYPE_REG
   536  		p2.From.Reg = loong64.REGTMP
   537  		p2.To.Type = obj.TYPE_MEM
   538  		p2.To.Reg = v.Args[0].Reg()
   539  		p3 := s.Prog(loong64.ABEQ)
   540  		p3.From.Type = obj.TYPE_REG
   541  		p3.From.Reg = loong64.REGTMP
   542  		p3.To.Type = obj.TYPE_BRANCH
   543  		p3.To.SetTarget(p)
   544  		s.Prog(loong64.ADBAR)
   545  	case ssa.OpLOONG64LoweredAtomicAdd32, ssa.OpLOONG64LoweredAtomicAdd64:
   546  		// DBAR
   547  		// LL	(Rarg0), Rout
   548  		// ADDV Rarg1, Rout, Rtmp
   549  		// SC	Rtmp, (Rarg0)
   550  		// BEQ	Rtmp, -3(PC)
   551  		// DBAR
   552  		// ADDV Rarg1, Rout
   553  		ll := loong64.ALLV
   554  		sc := loong64.ASCV
   555  		if v.Op == ssa.OpLOONG64LoweredAtomicAdd32 {
   556  			ll = loong64.ALL
   557  			sc = loong64.ASC
   558  		}
   559  		s.Prog(loong64.ADBAR)
   560  		p := s.Prog(ll)
   561  		p.From.Type = obj.TYPE_MEM
   562  		p.From.Reg = v.Args[0].Reg()
   563  		p.To.Type = obj.TYPE_REG
   564  		p.To.Reg = v.Reg0()
   565  		p1 := s.Prog(loong64.AADDVU)
   566  		p1.From.Type = obj.TYPE_REG
   567  		p1.From.Reg = v.Args[1].Reg()
   568  		p1.Reg = v.Reg0()
   569  		p1.To.Type = obj.TYPE_REG
   570  		p1.To.Reg = loong64.REGTMP
   571  		p2 := s.Prog(sc)
   572  		p2.From.Type = obj.TYPE_REG
   573  		p2.From.Reg = loong64.REGTMP
   574  		p2.To.Type = obj.TYPE_MEM
   575  		p2.To.Reg = v.Args[0].Reg()
   576  		p3 := s.Prog(loong64.ABEQ)
   577  		p3.From.Type = obj.TYPE_REG
   578  		p3.From.Reg = loong64.REGTMP
   579  		p3.To.Type = obj.TYPE_BRANCH
   580  		p3.To.SetTarget(p)
   581  		s.Prog(loong64.ADBAR)
   582  		p4 := s.Prog(loong64.AADDVU)
   583  		p4.From.Type = obj.TYPE_REG
   584  		p4.From.Reg = v.Args[1].Reg()
   585  		p4.Reg = v.Reg0()
   586  		p4.To.Type = obj.TYPE_REG
   587  		p4.To.Reg = v.Reg0()
   588  	case ssa.OpLOONG64LoweredAtomicAddconst32, ssa.OpLOONG64LoweredAtomicAddconst64:
   589  		// DBAR
   590  		// LL	(Rarg0), Rout
   591  		// ADDV $auxint, Rout, Rtmp
   592  		// SC	Rtmp, (Rarg0)
   593  		// BEQ	Rtmp, -3(PC)
   594  		// DBAR
   595  		// ADDV $auxint, Rout
   596  		ll := loong64.ALLV
   597  		sc := loong64.ASCV
   598  		if v.Op == ssa.OpLOONG64LoweredAtomicAddconst32 {
   599  			ll = loong64.ALL
   600  			sc = loong64.ASC
   601  		}
   602  		s.Prog(loong64.ADBAR)
   603  		p := s.Prog(ll)
   604  		p.From.Type = obj.TYPE_MEM
   605  		p.From.Reg = v.Args[0].Reg()
   606  		p.To.Type = obj.TYPE_REG
   607  		p.To.Reg = v.Reg0()
   608  		p1 := s.Prog(loong64.AADDVU)
   609  		p1.From.Type = obj.TYPE_CONST
   610  		p1.From.Offset = v.AuxInt
   611  		p1.Reg = v.Reg0()
   612  		p1.To.Type = obj.TYPE_REG
   613  		p1.To.Reg = loong64.REGTMP
   614  		p2 := s.Prog(sc)
   615  		p2.From.Type = obj.TYPE_REG
   616  		p2.From.Reg = loong64.REGTMP
   617  		p2.To.Type = obj.TYPE_MEM
   618  		p2.To.Reg = v.Args[0].Reg()
   619  		p3 := s.Prog(loong64.ABEQ)
   620  		p3.From.Type = obj.TYPE_REG
   621  		p3.From.Reg = loong64.REGTMP
   622  		p3.To.Type = obj.TYPE_BRANCH
   623  		p3.To.SetTarget(p)
   624  		s.Prog(loong64.ADBAR)
   625  		p4 := s.Prog(loong64.AADDVU)
   626  		p4.From.Type = obj.TYPE_CONST
   627  		p4.From.Offset = v.AuxInt
   628  		p4.Reg = v.Reg0()
   629  		p4.To.Type = obj.TYPE_REG
   630  		p4.To.Reg = v.Reg0()
   631  	case ssa.OpLOONG64LoweredAtomicCas32, ssa.OpLOONG64LoweredAtomicCas64:
   632  		// MOVV $0, Rout
   633  		// DBAR
   634  		// LL	(Rarg0), Rtmp
   635  		// BNE	Rtmp, Rarg1, 4(PC)
   636  		// MOVV Rarg2, Rout
   637  		// SC	Rout, (Rarg0)
   638  		// BEQ	Rout, -4(PC)
   639  		// DBAR
   640  		ll := loong64.ALLV
   641  		sc := loong64.ASCV
   642  		if v.Op == ssa.OpLOONG64LoweredAtomicCas32 {
   643  			ll = loong64.ALL
   644  			sc = loong64.ASC
   645  		}
   646  		p := s.Prog(loong64.AMOVV)
   647  		p.From.Type = obj.TYPE_REG
   648  		p.From.Reg = loong64.REGZERO
   649  		p.To.Type = obj.TYPE_REG
   650  		p.To.Reg = v.Reg0()
   651  		s.Prog(loong64.ADBAR)
   652  		p1 := s.Prog(ll)
   653  		p1.From.Type = obj.TYPE_MEM
   654  		p1.From.Reg = v.Args[0].Reg()
   655  		p1.To.Type = obj.TYPE_REG
   656  		p1.To.Reg = loong64.REGTMP
   657  		p2 := s.Prog(loong64.ABNE)
   658  		p2.From.Type = obj.TYPE_REG
   659  		p2.From.Reg = v.Args[1].Reg()
   660  		p2.Reg = loong64.REGTMP
   661  		p2.To.Type = obj.TYPE_BRANCH
   662  		p3 := s.Prog(loong64.AMOVV)
   663  		p3.From.Type = obj.TYPE_REG
   664  		p3.From.Reg = v.Args[2].Reg()
   665  		p3.To.Type = obj.TYPE_REG
   666  		p3.To.Reg = v.Reg0()
   667  		p4 := s.Prog(sc)
   668  		p4.From.Type = obj.TYPE_REG
   669  		p4.From.Reg = v.Reg0()
   670  		p4.To.Type = obj.TYPE_MEM
   671  		p4.To.Reg = v.Args[0].Reg()
   672  		p5 := s.Prog(loong64.ABEQ)
   673  		p5.From.Type = obj.TYPE_REG
   674  		p5.From.Reg = v.Reg0()
   675  		p5.To.Type = obj.TYPE_BRANCH
   676  		p5.To.SetTarget(p1)
   677  		p6 := s.Prog(loong64.ADBAR)
   678  		p2.To.SetTarget(p6)
   679  	case ssa.OpLOONG64LoweredNilCheck:
   680  		// Issue a load which will fault if arg is nil.
   681  		p := s.Prog(loong64.AMOVB)
   682  		p.From.Type = obj.TYPE_MEM
   683  		p.From.Reg = v.Args[0].Reg()
   684  		ssagen.AddAux(&p.From, v)
   685  		p.To.Type = obj.TYPE_REG
   686  		p.To.Reg = loong64.REGTMP
   687  		if logopt.Enabled() {
   688  			logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name)
   689  		}
   690  		if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
   691  			base.WarnfAt(v.Pos, "generated nil check")
   692  		}
   693  	case ssa.OpLOONG64FPFlagTrue,
   694  		ssa.OpLOONG64FPFlagFalse:
   695  		// MOVV	$0, r
   696  		// BFPF	2(PC)
   697  		// MOVV	$1, r
   698  		branch := loong64.ABFPF
   699  		if v.Op == ssa.OpLOONG64FPFlagFalse {
   700  			branch = loong64.ABFPT
   701  		}
   702  		p := s.Prog(loong64.AMOVV)
   703  		p.From.Type = obj.TYPE_REG
   704  		p.From.Reg = loong64.REGZERO
   705  		p.To.Type = obj.TYPE_REG
   706  		p.To.Reg = v.Reg()
   707  		p2 := s.Prog(branch)
   708  		p2.To.Type = obj.TYPE_BRANCH
   709  		p3 := s.Prog(loong64.AMOVV)
   710  		p3.From.Type = obj.TYPE_CONST
   711  		p3.From.Offset = 1
   712  		p3.To.Type = obj.TYPE_REG
   713  		p3.To.Reg = v.Reg()
   714  		p4 := s.Prog(obj.ANOP) // not a machine instruction, for branch to land
   715  		p2.To.SetTarget(p4)
   716  	case ssa.OpLOONG64LoweredGetClosurePtr:
   717  		// Closure pointer is R22 (loong64.REGCTXT).
   718  		ssagen.CheckLoweredGetClosurePtr(v)
   719  	case ssa.OpLOONG64LoweredGetCallerSP:
   720  		// caller's SP is FixedFrameSize below the address of the first arg
   721  		p := s.Prog(loong64.AMOVV)
   722  		p.From.Type = obj.TYPE_ADDR
   723  		p.From.Offset = -base.Ctxt.Arch.FixedFrameSize
   724  		p.From.Name = obj.NAME_PARAM
   725  		p.To.Type = obj.TYPE_REG
   726  		p.To.Reg = v.Reg()
   727  	case ssa.OpLOONG64LoweredGetCallerPC:
   728  		p := s.Prog(obj.AGETCALLERPC)
   729  		p.To.Type = obj.TYPE_REG
   730  		p.To.Reg = v.Reg()
   731  	case ssa.OpLOONG64MASKEQZ, ssa.OpLOONG64MASKNEZ:
   732  		p := s.Prog(v.Op.Asm())
   733  		p.From.Type = obj.TYPE_REG
   734  		p.From.Reg = v.Args[1].Reg()
   735  		p.Reg = v.Args[0].Reg()
   736  		p.To.Type = obj.TYPE_REG
   737  		p.To.Reg = v.Reg()
   738  	case ssa.OpClobber, ssa.OpClobberReg:
   739  		// TODO: implement for clobberdead experiment. Nop is ok for now.
   740  	default:
   741  		v.Fatalf("genValue not implemented: %s", v.LongString())
   742  	}
   743  }
   744  
   745  var blockJump = map[ssa.BlockKind]struct {
   746  	asm, invasm obj.As
   747  }{
   748  	ssa.BlockLOONG64EQ:  {loong64.ABEQ, loong64.ABNE},
   749  	ssa.BlockLOONG64NE:  {loong64.ABNE, loong64.ABEQ},
   750  	ssa.BlockLOONG64LTZ: {loong64.ABLTZ, loong64.ABGEZ},
   751  	ssa.BlockLOONG64GEZ: {loong64.ABGEZ, loong64.ABLTZ},
   752  	ssa.BlockLOONG64LEZ: {loong64.ABLEZ, loong64.ABGTZ},
   753  	ssa.BlockLOONG64GTZ: {loong64.ABGTZ, loong64.ABLEZ},
   754  	ssa.BlockLOONG64FPT: {loong64.ABFPT, loong64.ABFPF},
   755  	ssa.BlockLOONG64FPF: {loong64.ABFPF, loong64.ABFPT},
   756  }
   757  
   758  func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
   759  	switch b.Kind {
   760  	case ssa.BlockPlain:
   761  		if b.Succs[0].Block() != next {
   762  			p := s.Prog(obj.AJMP)
   763  			p.To.Type = obj.TYPE_BRANCH
   764  			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
   765  		}
   766  	case ssa.BlockDefer:
   767  		// defer returns in R19:
   768  		// 0 if we should continue executing
   769  		// 1 if we should jump to deferreturn call
   770  		p := s.Prog(loong64.ABNE)
   771  		p.From.Type = obj.TYPE_REG
   772  		p.From.Reg = loong64.REGZERO
   773  		p.Reg = loong64.REG_R19
   774  		p.To.Type = obj.TYPE_BRANCH
   775  		s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()})
   776  		if b.Succs[0].Block() != next {
   777  			p := s.Prog(obj.AJMP)
   778  			p.To.Type = obj.TYPE_BRANCH
   779  			s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
   780  		}
   781  	case ssa.BlockExit, ssa.BlockRetJmp:
   782  	case ssa.BlockRet:
   783  		s.Prog(obj.ARET)
   784  	case ssa.BlockLOONG64EQ, ssa.BlockLOONG64NE,
   785  		ssa.BlockLOONG64LTZ, ssa.BlockLOONG64GEZ,
   786  		ssa.BlockLOONG64LEZ, ssa.BlockLOONG64GTZ,
   787  		ssa.BlockLOONG64FPT, ssa.BlockLOONG64FPF:
   788  		jmp := blockJump[b.Kind]
   789  		var p *obj.Prog
   790  		switch next {
   791  		case b.Succs[0].Block():
   792  			p = s.Br(jmp.invasm, b.Succs[1].Block())
   793  		case b.Succs[1].Block():
   794  			p = s.Br(jmp.asm, b.Succs[0].Block())
   795  		default:
   796  			if b.Likely != ssa.BranchUnlikely {
   797  				p = s.Br(jmp.asm, b.Succs[0].Block())
   798  				s.Br(obj.AJMP, b.Succs[1].Block())
   799  			} else {
   800  				p = s.Br(jmp.invasm, b.Succs[1].Block())
   801  				s.Br(obj.AJMP, b.Succs[0].Block())
   802  			}
   803  		}
   804  		if !b.Controls[0].Type.IsFlags() {
   805  			p.From.Type = obj.TYPE_REG
   806  			p.From.Reg = b.Controls[0].Reg()
   807  		}
   808  	default:
   809  		b.Fatalf("branch not implemented: %s", b.LongString())
   810  	}
   811  }
   812  
   813  func loadRegResult(s *ssagen.State, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog {
   814  	p := s.Prog(loadByType(t, reg))
   815  	p.From.Type = obj.TYPE_MEM
   816  	p.From.Name = obj.NAME_AUTO
   817  	p.From.Sym = n.Linksym()
   818  	p.From.Offset = n.FrameOffset() + off
   819  	p.To.Type = obj.TYPE_REG
   820  	p.To.Reg = reg
   821  	return p
   822  }
   823  
   824  func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog {
   825  	p = pp.Append(p, storeByType(t, reg), obj.TYPE_REG, reg, 0, obj.TYPE_MEM, 0, n.FrameOffset()+off)
   826  	p.To.Name = obj.NAME_PARAM
   827  	p.To.Sym = n.Linksym()
   828  	p.Pos = p.Pos.WithNotStmt()
   829  	return p
   830  }