github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/ssa/_gen/LOONG64Ops.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 main
     6  
     7  import "strings"
     8  
     9  // Notes:
    10  //  - Integer types live in the low portion of registers. Upper portions are junk.
    11  //  - Boolean types use the low-order byte of a register. 0=false, 1=true.
    12  //    Upper bytes are junk.
    13  //  - *const instructions may use a constant larger than the instruction can encode.
    14  //    In this case the assembler expands to multiple instructions and uses tmp
    15  //    register (R23).
    16  
    17  // Suffixes encode the bit width of various instructions.
    18  // V (vlong)     = 64 bit
    19  // WU (word)     = 32 bit unsigned
    20  // W (word)      = 32 bit
    21  // H (half word) = 16 bit
    22  // HU            = 16 bit unsigned
    23  // B (byte)      = 8 bit
    24  // BU            = 8 bit unsigned
    25  // F (float)     = 32 bit float
    26  // D (double)    = 64 bit float
    27  
    28  // Note: registers not used in regalloc are not included in this list,
    29  // so that regmask stays within int64
    30  // Be careful when hand coding regmasks.
    31  var regNamesLOONG64 = []string{
    32  	"R0", // constant 0
    33  	"R1",
    34  	"SP", // aka R3
    35  	"R4",
    36  	"R5",
    37  	"R6",
    38  	"R7",
    39  	"R8",
    40  	"R9",
    41  	"R10",
    42  	"R11",
    43  	"R12",
    44  	"R13",
    45  	"R14",
    46  	"R15",
    47  	"R16",
    48  	"R17",
    49  	"R18",
    50  	"R19",
    51  	"R20",
    52  	"R21",
    53  	"g", // aka R22
    54  	"R23",
    55  	"R24",
    56  	"R25",
    57  	"R26",
    58  	"R27",
    59  	"R28",
    60  	"R29",
    61  	// R30 is REGTMP not used in regalloc
    62  	"R31",
    63  
    64  	"F0",
    65  	"F1",
    66  	"F2",
    67  	"F3",
    68  	"F4",
    69  	"F5",
    70  	"F6",
    71  	"F7",
    72  	"F8",
    73  	"F9",
    74  	"F10",
    75  	"F11",
    76  	"F12",
    77  	"F13",
    78  	"F14",
    79  	"F15",
    80  	"F16",
    81  	"F17",
    82  	"F18",
    83  	"F19",
    84  	"F20",
    85  	"F21",
    86  	"F22",
    87  	"F23",
    88  	"F24",
    89  	"F25",
    90  	"F26",
    91  	"F27",
    92  	"F28",
    93  	"F29",
    94  	"F30",
    95  	"F31",
    96  
    97  	// If you add registers, update asyncPreempt in runtime.
    98  
    99  	// pseudo-registers
   100  	"SB",
   101  }
   102  
   103  func init() {
   104  	// Make map from reg names to reg integers.
   105  	if len(regNamesLOONG64) > 64 {
   106  		panic("too many registers")
   107  	}
   108  	num := map[string]int{}
   109  	for i, name := range regNamesLOONG64 {
   110  		num[name] = i
   111  	}
   112  	buildReg := func(s string) regMask {
   113  		m := regMask(0)
   114  		for _, r := range strings.Split(s, " ") {
   115  			if n, ok := num[r]; ok {
   116  				m |= regMask(1) << uint(n)
   117  				continue
   118  			}
   119  			panic("register " + r + " not found")
   120  		}
   121  		return m
   122  	}
   123  
   124  	// Common individual register masks
   125  	var (
   126  		gp         = buildReg("R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R23 R24 R25 R26 R27 R28 R29 R31") // R1 is LR, R2 is thread pointer, R3 is stack pointer, R22 is g, R30 is REGTMP
   127  		gpg        = gp | buildReg("g")
   128  		gpsp       = gp | buildReg("SP")
   129  		gpspg      = gpg | buildReg("SP")
   130  		gpspsbg    = gpspg | buildReg("SB")
   131  		fp         = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31")
   132  		callerSave = gp | fp | buildReg("g") // runtime.setg (and anything calling it) may clobber g
   133  		r1         = buildReg("R20")
   134  		r2         = buildReg("R21")
   135  		r3         = buildReg("R23")
   136  		r4         = buildReg("R24")
   137  	)
   138  	// Common regInfo
   139  	var (
   140  		gp01      = regInfo{inputs: nil, outputs: []regMask{gp}}
   141  		gp11      = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
   142  		gp11sp    = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
   143  		gp21      = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}}
   144  		gpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}
   145  		gpstore   = regInfo{inputs: []regMask{gpspsbg, gpg}}
   146  		gpstore0  = regInfo{inputs: []regMask{gpspsbg}}
   147  		gpxchg    = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
   148  		gpcas     = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}}
   149  		fp01      = regInfo{inputs: nil, outputs: []regMask{fp}}
   150  		fp11      = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
   151  		fp21      = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
   152  		fp2flags  = regInfo{inputs: []regMask{fp, fp}}
   153  		fpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
   154  		fpstore   = regInfo{inputs: []regMask{gpspsbg, fp}}
   155  		readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
   156  	)
   157  	ops := []opData{
   158  		// binary ops
   159  		{name: "ADDV", argLength: 2, reg: gp21, asm: "ADDVU", commutative: true},   // arg0 + arg1
   160  		{name: "ADDVconst", argLength: 1, reg: gp11sp, asm: "ADDVU", aux: "Int64"}, // arg0 + auxInt. auxInt is 32-bit, also in other *const ops.
   161  		{name: "SUBV", argLength: 2, reg: gp21, asm: "SUBVU"},                      // arg0 - arg1
   162  		{name: "SUBVconst", argLength: 1, reg: gp11, asm: "SUBVU", aux: "Int64"},   // arg0 - auxInt
   163  
   164  		{name: "MULV", argLength: 2, reg: gp21, asm: "MULV", commutative: true, typ: "Int64"},      // arg0 * arg1
   165  		{name: "MULHV", argLength: 2, reg: gp21, asm: "MULHV", commutative: true, typ: "Int64"},    // (arg0 * arg1) >> 64, signed
   166  		{name: "MULHVU", argLength: 2, reg: gp21, asm: "MULHVU", commutative: true, typ: "UInt64"}, // (arg0 * arg1) >> 64, unsigned
   167  		{name: "DIVV", argLength: 2, reg: gp21, asm: "DIVV", typ: "Int64"},                         // arg0 / arg1, signed
   168  		{name: "DIVVU", argLength: 2, reg: gp21, asm: "DIVVU", typ: "UInt64"},                      // arg0 / arg1, unsigned
   169  		{name: "REMV", argLength: 2, reg: gp21, asm: "REMV", typ: "Int64"},                         // arg0 / arg1, signed
   170  		{name: "REMVU", argLength: 2, reg: gp21, asm: "REMVU", typ: "UInt64"},                      // arg0 / arg1, unsigned
   171  
   172  		{name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true}, // arg0 + arg1
   173  		{name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true}, // arg0 + arg1
   174  		{name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF"},                    // arg0 - arg1
   175  		{name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD"},                    // arg0 - arg1
   176  		{name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true}, // arg0 * arg1
   177  		{name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true}, // arg0 * arg1
   178  		{name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF"},                    // arg0 / arg1
   179  		{name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD"},                    // arg0 / arg1
   180  
   181  		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true},                // arg0 & arg1
   182  		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64"},                // arg0 & auxInt
   183  		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true},                  // arg0 | arg1
   184  		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64"},                  // arg0 | auxInt
   185  		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, typ: "UInt64"}, // arg0 ^ arg1
   186  		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64", typ: "UInt64"}, // arg0 ^ auxInt
   187  		{name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true},                // ^(arg0 | arg1)
   188  		{name: "NORconst", argLength: 1, reg: gp11, asm: "NOR", aux: "Int64"},                // ^(arg0 | auxInt)
   189  
   190  		{name: "NEGV", argLength: 1, reg: gp11},                // -arg0
   191  		{name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF"},   // -arg0, float32
   192  		{name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD"},   // -arg0, float64
   193  		{name: "SQRTD", argLength: 1, reg: fp11, asm: "SQRTD"}, // sqrt(arg0), float64
   194  		{name: "SQRTF", argLength: 1, reg: fp11, asm: "SQRTF"}, // sqrt(arg0), float32
   195  
   196  		{name: "MASKEQZ", argLength: 2, reg: gp21, asm: "MASKEQZ"}, // returns 0 if arg1 == 0, otherwise returns arg0
   197  		{name: "MASKNEZ", argLength: 2, reg: gp21, asm: "MASKNEZ"}, // returns 0 if arg1 != 0, otherwise returns arg0
   198  
   199  		// shifts
   200  		{name: "SLLV", argLength: 2, reg: gp21, asm: "SLLV"},                      // arg0 << arg1, shift amount is mod 64
   201  		{name: "SLLVconst", argLength: 1, reg: gp11, asm: "SLLV", aux: "Int64"},   // arg0 << auxInt
   202  		{name: "SRLV", argLength: 2, reg: gp21, asm: "SRLV"},                      // arg0 >> arg1, unsigned, shift amount is mod 64
   203  		{name: "SRLVconst", argLength: 1, reg: gp11, asm: "SRLV", aux: "Int64"},   // arg0 >> auxInt, unsigned
   204  		{name: "SRAV", argLength: 2, reg: gp21, asm: "SRAV"},                      // arg0 >> arg1, signed, shift amount is mod 64
   205  		{name: "SRAVconst", argLength: 1, reg: gp11, asm: "SRAV", aux: "Int64"},   // arg0 >> auxInt, signed
   206  		{name: "ROTR", argLength: 2, reg: gp21, asm: "ROTR"},                      // arg0 right rotate by (arg1 mod 32) bits
   207  		{name: "ROTRV", argLength: 2, reg: gp21, asm: "ROTRV"},                    // arg0 right rotate by (arg1 mod 64) bits
   208  		{name: "ROTRconst", argLength: 1, reg: gp11, asm: "ROTR", aux: "Int64"},   // uint32(arg0) right rotate by auxInt bits, auxInt should be in the range 0 to 31.
   209  		{name: "ROTRVconst", argLength: 1, reg: gp11, asm: "ROTRV", aux: "Int64"}, // arg0 right rotate by auxInt bits, auxInt should be in the range 0 to 63.
   210  
   211  		// comparisons
   212  		{name: "SGT", argLength: 2, reg: gp21, asm: "SGT", typ: "Bool"},                      // 1 if arg0 > arg1 (signed), 0 otherwise
   213  		{name: "SGTconst", argLength: 1, reg: gp11, asm: "SGT", aux: "Int64", typ: "Bool"},   // 1 if auxInt > arg0 (signed), 0 otherwise
   214  		{name: "SGTU", argLength: 2, reg: gp21, asm: "SGTU", typ: "Bool"},                    // 1 if arg0 > arg1 (unsigned), 0 otherwise
   215  		{name: "SGTUconst", argLength: 1, reg: gp11, asm: "SGTU", aux: "Int64", typ: "Bool"}, // 1 if auxInt > arg0 (unsigned), 0 otherwise
   216  
   217  		{name: "CMPEQF", argLength: 2, reg: fp2flags, asm: "CMPEQF", typ: "Flags"}, // flags=true if arg0 = arg1, float32
   218  		{name: "CMPEQD", argLength: 2, reg: fp2flags, asm: "CMPEQD", typ: "Flags"}, // flags=true if arg0 = arg1, float64
   219  		{name: "CMPGEF", argLength: 2, reg: fp2flags, asm: "CMPGEF", typ: "Flags"}, // flags=true if arg0 >= arg1, float32
   220  		{name: "CMPGED", argLength: 2, reg: fp2flags, asm: "CMPGED", typ: "Flags"}, // flags=true if arg0 >= arg1, float64
   221  		{name: "CMPGTF", argLength: 2, reg: fp2flags, asm: "CMPGTF", typ: "Flags"}, // flags=true if arg0 > arg1, float32
   222  		{name: "CMPGTD", argLength: 2, reg: fp2flags, asm: "CMPGTD", typ: "Flags"}, // flags=true if arg0 > arg1, float64
   223  
   224  		// moves
   225  		{name: "MOVVconst", argLength: 0, reg: gp01, aux: "Int64", asm: "MOVV", typ: "UInt64", rematerializeable: true},    // auxint
   226  		{name: "MOVFconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVF", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float
   227  		{name: "MOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float
   228  
   229  		{name: "MOVVaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP") | buildReg("SB")}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVV", rematerializeable: true, symEffect: "Addr"}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB
   230  
   231  		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"},     // load from arg0 + auxInt + aux.  arg1=mem.
   232  		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   233  		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"},    // load from arg0 + auxInt + aux.  arg1=mem.
   234  		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux.  arg1=mem.
   235  		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true, symEffect: "Read"},    // load from arg0 + auxInt + aux.  arg1=mem.
   236  		{name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux.  arg1=mem.
   237  		{name: "MOVVload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVV", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"},   // load from arg0 + auxInt + aux.  arg1=mem.
   238  		{name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   239  		{name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   240  
   241  		{name: "MOVBstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 1 byte of arg1 to arg0 + auxInt + aux.  arg2=mem.
   242  		{name: "MOVHstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 2 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   243  		{name: "MOVWstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   244  		{name: "MOVVstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVV", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   245  		{name: "MOVFstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVF", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   246  		{name: "MOVDstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   247  
   248  		{name: "MOVBstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 1 byte of zero to arg0 + auxInt + aux.  arg1=mem.
   249  		{name: "MOVHstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 2 bytes of zero to arg0 + auxInt + aux.  arg1=mem.
   250  		{name: "MOVWstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes of zero to arg0 + auxInt + aux.  arg1=mem.
   251  		{name: "MOVVstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVV", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of zero to arg0 + auxInt + aux.  ar12=mem.
   252  
   253  		// conversions
   254  		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"},   // move from arg0, sign-extended from byte
   255  		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte
   256  		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"},   // move from arg0, sign-extended from half
   257  		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half
   258  		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"},   // move from arg0, sign-extended from word
   259  		{name: "MOVWUreg", argLength: 1, reg: gp11, asm: "MOVWU"}, // move from arg0, unsign-extended from word
   260  		{name: "MOVVreg", argLength: 1, reg: gp11, asm: "MOVV"},   // move from arg0
   261  
   262  		{name: "MOVVnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register
   263  
   264  		{name: "MOVWF", argLength: 1, reg: fp11, asm: "MOVWF"},     // int32 -> float32
   265  		{name: "MOVWD", argLength: 1, reg: fp11, asm: "MOVWD"},     // int32 -> float64
   266  		{name: "MOVVF", argLength: 1, reg: fp11, asm: "MOVVF"},     // int64 -> float32
   267  		{name: "MOVVD", argLength: 1, reg: fp11, asm: "MOVVD"},     // int64 -> float64
   268  		{name: "TRUNCFW", argLength: 1, reg: fp11, asm: "TRUNCFW"}, // float32 -> int32
   269  		{name: "TRUNCDW", argLength: 1, reg: fp11, asm: "TRUNCDW"}, // float64 -> int32
   270  		{name: "TRUNCFV", argLength: 1, reg: fp11, asm: "TRUNCFV"}, // float32 -> int64
   271  		{name: "TRUNCDV", argLength: 1, reg: fp11, asm: "TRUNCDV"}, // float64 -> int64
   272  		{name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD"},     // float32 -> float64
   273  		{name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"},     // float64 -> float32
   274  
   275  		// function calls
   276  		{name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                               // call static function aux.(*obj.LSym).  last arg=mem, auxint=argsize, returns mem
   277  		{name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true},                                 // tail call static function aux.(*obj.LSym).  last arg=mem, auxint=argsize, returns mem
   278  		{name: "CALLclosure", argLength: -1, reg: regInfo{inputs: []regMask{gpsp, buildReg("R29"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, last arg=mem, auxint=argsize, returns mem
   279  		{name: "CALLinter", argLength: -1, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                         // call fn by pointer.  arg0=codeptr, last arg=mem, auxint=argsize, returns mem
   280  
   281  		// duffzero
   282  		// arg0 = address of memory to zero
   283  		// arg1 = mem
   284  		// auxint = offset into duffzero code to start executing
   285  		// returns mem
   286  		// R20 aka loong64.REGRT1 changed as side effect
   287  		{
   288  			name:      "DUFFZERO",
   289  			aux:       "Int64",
   290  			argLength: 2,
   291  			reg: regInfo{
   292  				inputs:   []regMask{buildReg("R20")},
   293  				clobbers: buildReg("R20 R1"),
   294  			},
   295  			typ:            "Mem",
   296  			faultOnNilArg0: true,
   297  		},
   298  
   299  		// duffcopy
   300  		// arg0 = address of dst memory (in R21, changed as side effect)
   301  		// arg1 = address of src memory (in R20, changed as side effect)
   302  		// arg2 = mem
   303  		// auxint = offset into duffcopy code to start executing
   304  		// returns mem
   305  		{
   306  			name:      "DUFFCOPY",
   307  			aux:       "Int64",
   308  			argLength: 3,
   309  			reg: regInfo{
   310  				inputs:   []regMask{buildReg("R21"), buildReg("R20")},
   311  				clobbers: buildReg("R20 R21 R1"),
   312  			},
   313  			typ:            "Mem",
   314  			faultOnNilArg0: true,
   315  			faultOnNilArg1: true,
   316  		},
   317  
   318  		// large or unaligned zeroing
   319  		// arg0 = address of memory to zero (in R20, changed as side effect)
   320  		// arg1 = address of the last element to zero
   321  		// arg2 = mem
   322  		// auxint = alignment
   323  		// returns mem
   324  		//	MOVx	R0, (R20)
   325  		//	ADDV	$sz, R20
   326  		//	BGEU	Rarg1, R20, -2(PC)
   327  		{
   328  			name:      "LoweredZero",
   329  			aux:       "Int64",
   330  			argLength: 3,
   331  			reg: regInfo{
   332  				inputs:   []regMask{buildReg("R20"), gp},
   333  				clobbers: buildReg("R20"),
   334  			},
   335  			typ:            "Mem",
   336  			faultOnNilArg0: true,
   337  		},
   338  
   339  		// large or unaligned move
   340  		// arg0 = address of dst memory (in R21, changed as side effect)
   341  		// arg1 = address of src memory (in R20, changed as side effect)
   342  		// arg2 = address of the last element of src
   343  		// arg3 = mem
   344  		// auxint = alignment
   345  		// returns mem
   346  		//	MOVx	(R20), Rtmp
   347  		//	MOVx	Rtmp, (R21)
   348  		//	ADDV	$sz, R20
   349  		//	ADDV	$sz, R21
   350  		//	BGEU	Rarg2, R20, -4(PC)
   351  		{
   352  			name:      "LoweredMove",
   353  			aux:       "Int64",
   354  			argLength: 4,
   355  			reg: regInfo{
   356  				inputs:   []regMask{buildReg("R21"), buildReg("R20"), gp},
   357  				clobbers: buildReg("R20 R21"),
   358  			},
   359  			typ:            "Mem",
   360  			faultOnNilArg0: true,
   361  			faultOnNilArg1: true,
   362  		},
   363  
   364  		// atomic loads.
   365  		// load from arg0. arg1=mem.
   366  		// returns <value,memory> so they can be properly ordered with other loads.
   367  		{name: "LoweredAtomicLoad8", argLength: 2, reg: gpload, faultOnNilArg0: true},
   368  		{name: "LoweredAtomicLoad32", argLength: 2, reg: gpload, faultOnNilArg0: true},
   369  		{name: "LoweredAtomicLoad64", argLength: 2, reg: gpload, faultOnNilArg0: true},
   370  
   371  		// atomic stores.
   372  		// store arg1 to arg0. arg2=mem. returns memory.
   373  		{name: "LoweredAtomicStore8", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   374  		{name: "LoweredAtomicStore32", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   375  		{name: "LoweredAtomicStore64", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   376  		// store zero to arg0. arg1=mem. returns memory.
   377  		{name: "LoweredAtomicStorezero32", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true},
   378  		{name: "LoweredAtomicStorezero64", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true},
   379  
   380  		// atomic exchange.
   381  		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>.
   382  		// DBAR
   383  		// LL	(Rarg0), Rout
   384  		// MOVV Rarg1, Rtmp
   385  		// SC	Rtmp, (Rarg0)
   386  		// BEQ	Rtmp, -3(PC)
   387  		// DBAR
   388  		{name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   389  		{name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   390  
   391  		// atomic add.
   392  		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>.
   393  		// DBAR
   394  		// LL	(Rarg0), Rout
   395  		// ADDV Rarg1, Rout, Rtmp
   396  		// SC	Rtmp, (Rarg0)
   397  		// BEQ	Rtmp, -3(PC)
   398  		// DBAR
   399  		// ADDV Rarg1, Rout
   400  		{name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   401  		{name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   402  		// *arg0 += auxint. arg1=mem. returns <new content of *arg0, memory>. auxint is 32-bit.
   403  		{name: "LoweredAtomicAddconst32", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int32", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   404  		{name: "LoweredAtomicAddconst64", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int64", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   405  
   406  		// atomic compare and swap.
   407  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
   408  		// if *arg0 == arg1 {
   409  		//   *arg0 = arg2
   410  		//   return (true, memory)
   411  		// } else {
   412  		//   return (false, memory)
   413  		// }
   414  		// DBAR
   415  		// MOVV $0, Rout
   416  		// LL	(Rarg0), Rtmp
   417  		// BNE	Rtmp, Rarg1, 4(PC)
   418  		// MOVV Rarg2, Rout
   419  		// SC	Rout, (Rarg0)
   420  		// BEQ	Rout, -4(PC)
   421  		// DBAR
   422  		{name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   423  		{name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   424  
   425  		// pseudo-ops
   426  		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil.  arg1=mem.
   427  
   428  		{name: "FPFlagTrue", argLength: 1, reg: readflags},  // bool, true if FP flag is true
   429  		{name: "FPFlagFalse", argLength: 1, reg: readflags}, // bool, true if FP flag is false
   430  
   431  		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   432  		// and sorts it to the very beginning of the block to prevent other
   433  		// use of R22 (loong64.REGCTXT, the closure pointer)
   434  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R29")}}, zeroWidth: true},
   435  
   436  		// LoweredGetCallerSP returns the SP of the caller of the current function. arg0=mem.
   437  		{name: "LoweredGetCallerSP", argLength: 1, reg: gp01, rematerializeable: true},
   438  
   439  		// LoweredGetCallerPC evaluates to the PC to which its "caller" will return.
   440  		// I.e., if f calls g "calls" getcallerpc,
   441  		// the result should be the PC within f that g will return to.
   442  		// See runtime/stubs.go for a more detailed discussion.
   443  		{name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true},
   444  
   445  		// LoweredWB invokes runtime.gcWriteBarrier. arg0=mem, auxint=# of buffer entries needed
   446  		// It saves all GP registers if necessary,
   447  		// but clobbers R1 (LR) because it's a call
   448  		// and R30 (REGTMP).
   449  		// Returns a pointer to a write barrier buffer in R29.
   450  		{name: "LoweredWB", argLength: 1, reg: regInfo{clobbers: (callerSave &^ gpg) | buildReg("R1"), outputs: []regMask{buildReg("R29")}}, clobberFlags: true, aux: "Int64"},
   451  
   452  		// There are three of these functions so that they can have three different register inputs.
   453  		// When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the
   454  		// default registers to match so we don't need to copy registers around unnecessarily.
   455  		{name: "LoweredPanicBoundsA", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r3, r4}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go).
   456  		{name: "LoweredPanicBoundsB", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r2, r3}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go).
   457  		{name: "LoweredPanicBoundsC", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r1, r2}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in genericOps.go).
   458  	}
   459  
   460  	blocks := []blockData{
   461  		{name: "EQ", controls: 1},
   462  		{name: "NE", controls: 1},
   463  		{name: "LTZ", controls: 1}, // < 0
   464  		{name: "LEZ", controls: 1}, // <= 0
   465  		{name: "GTZ", controls: 1}, // > 0
   466  		{name: "GEZ", controls: 1}, // >= 0
   467  		{name: "FPT", controls: 1}, // FP flag is true
   468  		{name: "FPF", controls: 1}, // FP flag is false
   469  	}
   470  
   471  	archs = append(archs, arch{
   472  		name:     "LOONG64",
   473  		pkg:      "github.com/go-asm/go/cmd/obj/loong64",
   474  		genfile:  "../../loong64/ssa.go",
   475  		ops:      ops,
   476  		blocks:   blocks,
   477  		regnames: regNamesLOONG64,
   478  		// TODO: support register ABI on loong64
   479  		ParamIntRegNames:   "R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19",
   480  		ParamFloatRegNames: "F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15",
   481  		gpregmask:          gp,
   482  		fpregmask:          fp,
   483  		framepointerreg:    -1, // not used
   484  		linkreg:            int8(num["R1"]),
   485  	})
   486  }