Source file src/cmd/compile/internal/ssa/_gen/LOONG64Ops.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 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  	"ZERO", // 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{}
   114  		for _, r := range strings.Split(s, " ") {
   115  			if n, ok := num[r]; ok {
   116  				m = m.addReg(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.union(buildReg("g"))
   128  		gpsp       = gp.union(buildReg("SP"))
   129  		gpspg      = gpg.union(buildReg("SP"))
   130  		gpspsbg    = gpspg.union(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.union(fp).union(buildReg("g")) // runtime.setg (and anything calling it) may clobber g
   133  		first16    = buildReg("R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19")
   134  		rz         = buildReg("ZERO")
   135  	)
   136  	// Common regInfo
   137  	var (
   138  		gp01      = regInfo{inputs: nil, outputs: []regMask{gp}}
   139  		gp11      = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
   140  		gp11sp    = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
   141  		gp21      = regInfo{inputs: []regMask{gpg, gpg.union(rz)}, outputs: []regMask{gp}}
   142  		gpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}
   143  		gp2load   = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
   144  		gpstore   = regInfo{inputs: []regMask{gpspsbg, gpg}}
   145  		gpstore2  = regInfo{inputs: []regMask{gpspsbg, gpg, gpg.union(rz)}}
   146  		gpoldatom = regInfo{inputs: []regMask{gpspsbg, gpg}}
   147  		gpxchg    = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
   148  		gpcas     = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}}
   149  		preldreg  = regInfo{inputs: []regMask{gpspg}}
   150  		fp01      = regInfo{inputs: nil, outputs: []regMask{fp}}
   151  		fp11      = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
   152  		fp21      = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
   153  		fp31      = regInfo{inputs: []regMask{fp, fp, fp}, outputs: []regMask{fp}}
   154  		fp2flags  = regInfo{inputs: []regMask{fp, fp}}
   155  		fpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
   156  		fp2load   = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{fp}}
   157  		fpstore   = regInfo{inputs: []regMask{gpspsbg, fp}}
   158  		fpstore2  = regInfo{inputs: []regMask{gpspsbg, gpg, fp}}
   159  		fpgp      = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}
   160  		gpfp      = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}
   161  		readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
   162  	)
   163  	ops := []opData{
   164  		// unary ops
   165  		{name: "NEGV", argLength: 1, reg: gp11, earlyOk: true},              // -arg0
   166  		{name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF", earlyOk: true}, // -arg0, float32
   167  		{name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD", earlyOk: true}, // -arg0, float64
   168  
   169  		{name: "SQRTD", argLength: 1, reg: fp11, asm: "SQRTD", earlyOk: true}, // sqrt(arg0), float64
   170  		{name: "SQRTF", argLength: 1, reg: fp11, asm: "SQRTF", earlyOk: true}, // sqrt(arg0), float32
   171  
   172  		{name: "ABSF", argLength: 1, reg: fp11, asm: "ABSF", earlyOk: true}, // abs(arg0), float32
   173  		{name: "ABSD", argLength: 1, reg: fp11, asm: "ABSD", earlyOk: true}, // abs(arg0), float64
   174  
   175  		{name: "CLZW", argLength: 1, reg: gp11, asm: "CLZW", earlyOk: true}, // Count leading (high order) zeroes (returns 0-32)
   176  		{name: "CLZV", argLength: 1, reg: gp11, asm: "CLZV", earlyOk: true}, // Count leading (high order) zeroes (returns 0-64)
   177  		{name: "CTZW", argLength: 1, reg: gp11, asm: "CTZW", earlyOk: true}, // Count trailing (low order) zeroes (returns 0-32)
   178  		{name: "CTZV", argLength: 1, reg: gp11, asm: "CTZV", earlyOk: true}, // Count trailing (low order) zeroes (returns 0-64)
   179  
   180  		{name: "REVB2H", argLength: 1, reg: gp11, asm: "REVB2H", earlyOk: true}, // Swap bytes: 0x11223344 -> 0x22114433 (sign extends to 64 bits)
   181  		{name: "REVB2W", argLength: 1, reg: gp11, asm: "REVB2W", earlyOk: true}, // Swap bytes: 0x1122334455667788 -> 0x4433221188776655
   182  		{name: "REVB4H", argLength: 1, reg: gp11, asm: "REVB4H", earlyOk: true}, // Swap bytes: 0x1122334455667788 -> 0x2211443366558877
   183  		{name: "REVBV", argLength: 1, reg: gp11, asm: "REVBV", earlyOk: true},   // Swap bytes: 0x1122334455667788 -> 0x8877665544332211
   184  
   185  		{name: "BITREV4B", argLength: 1, reg: gp11, asm: "BITREV4B", earlyOk: true}, // Reverse the bits of each byte inside a 32-bit arg[0]
   186  		{name: "BITREVW", argLength: 1, reg: gp11, asm: "BITREVW", earlyOk: true},   // Reverse the bits in a 32-bit arg[0]
   187  		{name: "BITREVV", argLength: 1, reg: gp11, asm: "BITREVV", earlyOk: true},   // Reverse the bits in a 64-bit arg[0]
   188  
   189  		// Note: these must be preceded by a check of the LSX bit
   190  		{name: "VPCNT64", argLength: 1, reg: fp11, asm: "VPCNTV"}, // count set bits for each 64-bit unit and store the result in each 64-bit unit
   191  		{name: "VPCNT32", argLength: 1, reg: fp11, asm: "VPCNTW"}, // count set bits for each 32-bit unit and store the result in each 32-bit unit
   192  		{name: "VPCNT16", argLength: 1, reg: fp11, asm: "VPCNTH"}, // count set bits for each 16-bit unit and store the result in each 16-bit unit
   193  
   194  		// binary ops
   195  		{name: "ADDV", argLength: 2, reg: gp21, asm: "ADDVU", commutative: true, earlyOk: true},      // arg0 + arg1
   196  		{name: "ADDVconst", argLength: 1, reg: gp11sp, asm: "ADDVU", aux: "Int64", earlyOk: true},    // arg0 + auxInt. auxInt is 32-bit, also in other *const ops.
   197  		{name: "ADDV16const", argLength: 1, reg: gp11sp, asm: "ADDV16", aux: "Int64", earlyOk: true}, // arg0 + auxInt. auxInt is signed 32-bit and is a multiple of 65536, also in other *const ops.
   198  		{name: "SUBV", argLength: 2, reg: gp21, asm: "SUBVU", earlyOk: true},                         // arg0 - arg1
   199  		{name: "SUBVconst", argLength: 1, reg: gp11, asm: "SUBVU", aux: "Int64", earlyOk: true},      // arg0 - auxInt
   200  
   201  		{name: "MULV", argLength: 2, reg: gp21, asm: "MULV", commutative: true, typ: "Int64", earlyOk: true},      // arg0 * arg1
   202  		{name: "MULHV", argLength: 2, reg: gp21, asm: "MULHV", commutative: true, typ: "Int64", earlyOk: true},    // (arg0 * arg1) >> 64, signed
   203  		{name: "MULHVU", argLength: 2, reg: gp21, asm: "MULHVU", commutative: true, typ: "UInt64", earlyOk: true}, // (arg0 * arg1) >> 64, unsigned
   204  		{name: "MULH", argLength: 2, reg: gp21, asm: "MULH", commutative: true, typ: "Int32", earlyOk: true},      // (arg0 * arg1) >> 32, signed
   205  		{name: "MULHU", argLength: 2, reg: gp21, asm: "MULHU", commutative: true, typ: "UInt32", earlyOk: true},   // (arg0 * arg1) >> 32, unsigned
   206  		{name: "DIVV", argLength: 2, reg: gp21, asm: "DIVV", typ: "Int64"},                                        // arg0 / arg1, signed
   207  		{name: "DIVVU", argLength: 2, reg: gp21, asm: "DIVVU", typ: "UInt64"},                                     // arg0 / arg1, unsigned
   208  		{name: "REMV", argLength: 2, reg: gp21, asm: "REMV", typ: "Int64"},                                        // arg0 / arg1, signed
   209  		{name: "REMVU", argLength: 2, reg: gp21, asm: "REMVU", typ: "UInt64"},                                     // arg0 / arg1, unsigned
   210  		{name: "MULWVW", argLength: 2, reg: gp21, asm: "MULWVW", commutative: true, earlyOk: true},                // arg0 * arg1, signed, 32-bit mult results in 64-bit
   211  		{name: "MULWVWU", argLength: 2, reg: gp21, asm: "MULWVWU", commutative: true, earlyOk: true},              // arg0 * arg1, unsigned, 32-bit mult results in 64-bit
   212  
   213  		{name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true, earlyOk: true}, // arg0 + arg1
   214  		{name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true, earlyOk: true}, // arg0 + arg1
   215  		{name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF", earlyOk: true},                    // arg0 - arg1
   216  		{name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD", earlyOk: true},                    // arg0 - arg1
   217  		{name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true, earlyOk: true}, // arg0 * arg1
   218  		{name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true, earlyOk: true}, // arg0 * arg1
   219  		{name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF"},                                   // arg0 / arg1
   220  		{name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD"},                                   // arg0 / arg1
   221  
   222  		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true, earlyOk: true},                // arg0 & arg1
   223  		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64", earlyOk: true},                // arg0 & auxInt
   224  		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true, earlyOk: true},                  // arg0 | arg1
   225  		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64", earlyOk: true},                  // arg0 | auxInt
   226  		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, typ: "UInt64", earlyOk: true}, // arg0 ^ arg1
   227  		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64", typ: "UInt64", earlyOk: true}, // arg0 ^ auxInt
   228  		{name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true, earlyOk: true},                // ^(arg0 | arg1)
   229  		{name: "NORconst", argLength: 1, reg: gp11, asm: "NOR", aux: "Int64", earlyOk: true},                // ^(arg0 | auxInt)
   230  		{name: "ANDN", argLength: 2, reg: gp21, asm: "ANDN", earlyOk: true},                                 // arg0 & ^arg1
   231  		{name: "ORN", argLength: 2, reg: gp21, asm: "ORN", earlyOk: true},                                   // arg0 | ^arg1
   232  
   233  		{name: "FMADDF", argLength: 3, reg: fp31, asm: "FMADDF", commutative: true, typ: "Float32", earlyOk: true},   // (arg0 * arg1) + arg2
   234  		{name: "FMADDD", argLength: 3, reg: fp31, asm: "FMADDD", commutative: true, typ: "Float64", earlyOk: true},   // (arg0 * arg1) + arg2
   235  		{name: "FMSUBF", argLength: 3, reg: fp31, asm: "FMSUBF", commutative: true, typ: "Float32", earlyOk: true},   // (arg0 * arg1) - arg2
   236  		{name: "FMSUBD", argLength: 3, reg: fp31, asm: "FMSUBD", commutative: true, typ: "Float64", earlyOk: true},   // (arg0 * arg1) - arg2
   237  		{name: "FNMADDF", argLength: 3, reg: fp31, asm: "FNMADDF", commutative: true, typ: "Float32", earlyOk: true}, // -((arg0 * arg1) + arg2)
   238  		{name: "FNMADDD", argLength: 3, reg: fp31, asm: "FNMADDD", commutative: true, typ: "Float64", earlyOk: true}, // -((arg0 * arg1) + arg2)
   239  		{name: "FNMSUBF", argLength: 3, reg: fp31, asm: "FNMSUBF", commutative: true, typ: "Float32", earlyOk: true}, // -((arg0 * arg1) - arg2)
   240  		{name: "FNMSUBD", argLength: 3, reg: fp31, asm: "FNMSUBD", commutative: true, typ: "Float64", earlyOk: true}, // -((arg0 * arg1) - arg2)
   241  
   242  		{name: "FMINF", argLength: 2, reg: fp21, resultNotInArgs: true, asm: "FMINF", commutative: true, typ: "Float32", earlyOk: true}, // min(arg0, arg1), float32
   243  		{name: "FMIND", argLength: 2, reg: fp21, resultNotInArgs: true, asm: "FMIND", commutative: true, typ: "Float64", earlyOk: true}, // min(arg0, arg1), float64
   244  		{name: "FMAXF", argLength: 2, reg: fp21, resultNotInArgs: true, asm: "FMAXF", commutative: true, typ: "Float32", earlyOk: true}, // max(arg0, arg1), float32
   245  		{name: "FMAXD", argLength: 2, reg: fp21, resultNotInArgs: true, asm: "FMAXD", commutative: true, typ: "Float64", earlyOk: true}, // max(arg0, arg1), float64
   246  
   247  		{name: "MASKEQZ", argLength: 2, reg: gp21, asm: "MASKEQZ", earlyOk: true},   // returns 0 if arg1 == 0, otherwise returns arg0
   248  		{name: "MASKNEZ", argLength: 2, reg: gp21, asm: "MASKNEZ", earlyOk: true},   // returns 0 if arg1 != 0, otherwise returns arg0
   249  		{name: "FCOPYSGD", argLength: 2, reg: fp21, asm: "FCOPYSGD", earlyOk: true}, // float64
   250  
   251  		// shifts
   252  		{name: "SLL", argLength: 2, reg: gp21, asm: "SLL", earlyOk: true},                        // arg0 << arg1, shift amount is mod 32
   253  		{name: "SLLV", argLength: 2, reg: gp21, asm: "SLLV", earlyOk: true},                      // arg0 << arg1, shift amount is mod 64
   254  		{name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int64", earlyOk: true},     // arg0 << auxInt, auxInt should be in the range 0 to 31.
   255  		{name: "SLLVconst", argLength: 1, reg: gp11, asm: "SLLV", aux: "Int64", earlyOk: true},   // arg0 << auxInt, auxInt should be in the range 0 to 63.
   256  		{name: "SRL", argLength: 2, reg: gp21, asm: "SRL", earlyOk: true},                        // arg0 >> arg1, shift amount is mod 32
   257  		{name: "SRLV", argLength: 2, reg: gp21, asm: "SRLV", earlyOk: true},                      // arg0 >> arg1, unsigned, shift amount is mod 64
   258  		{name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int64", earlyOk: true},     // arg0 >> auxInt, auxInt should be in the range 0 to 31.
   259  		{name: "SRLVconst", argLength: 1, reg: gp11, asm: "SRLV", aux: "Int64", earlyOk: true},   // arg0 >> auxInt, unsigned
   260  		{name: "SRA", argLength: 2, reg: gp21, asm: "SRA", earlyOk: true},                        // arg0 >> arg1, shift amount is mod 32
   261  		{name: "SRAV", argLength: 2, reg: gp21, asm: "SRAV", earlyOk: true},                      // arg0 >> arg1, signed, shift amount is mod 64
   262  		{name: "SRAconst", argLength: 1, reg: gp11, asm: "SRA", aux: "Int64", earlyOk: true},     // arg0 >> auxInt, signed, auxInt should be in the range 0 to 31.
   263  		{name: "SRAVconst", argLength: 1, reg: gp11, asm: "SRAV", aux: "Int64", earlyOk: true},   // arg0 >> auxInt, signed
   264  		{name: "ROTR", argLength: 2, reg: gp21, asm: "ROTR", earlyOk: true},                      // arg0 right rotate by (arg1 mod 32) bits
   265  		{name: "ROTRV", argLength: 2, reg: gp21, asm: "ROTRV", earlyOk: true},                    // arg0 right rotate by (arg1 mod 64) bits
   266  		{name: "ROTRconst", argLength: 1, reg: gp11, asm: "ROTR", aux: "Int64", earlyOk: true},   // uint32(arg0) right rotate by auxInt bits, auxInt should be in the range 0 to 31.
   267  		{name: "ROTRVconst", argLength: 1, reg: gp11, asm: "ROTRV", aux: "Int64", earlyOk: true}, // arg0 right rotate by auxInt bits, auxInt should be in the range 0 to 63.
   268  
   269  		// comparisons
   270  		{name: "SGT", argLength: 2, reg: gp21, asm: "SGT", typ: "Bool", earlyOk: true},                      // 1 if arg0 > arg1 (signed), 0 otherwise
   271  		{name: "SGTconst", argLength: 1, reg: gp11, asm: "SGT", aux: "Int64", typ: "Bool", earlyOk: true},   // 1 if auxInt > arg0 (signed), 0 otherwise
   272  		{name: "SGTU", argLength: 2, reg: gp21, asm: "SGTU", typ: "Bool", earlyOk: true},                    // 1 if arg0 > arg1 (unsigned), 0 otherwise
   273  		{name: "SGTUconst", argLength: 1, reg: gp11, asm: "SGTU", aux: "Int64", typ: "Bool", earlyOk: true}, // 1 if auxInt > arg0 (unsigned), 0 otherwise
   274  
   275  		{name: "CMPEQF", argLength: 2, reg: fp2flags, asm: "CMPEQF", typ: "Flags"}, // flags=true if arg0 = arg1, float32
   276  		{name: "CMPEQD", argLength: 2, reg: fp2flags, asm: "CMPEQD", typ: "Flags"}, // flags=true if arg0 = arg1, float64
   277  		{name: "CMPGEF", argLength: 2, reg: fp2flags, asm: "CMPGEF", typ: "Flags"}, // flags=true if arg0 >= arg1, float32
   278  		{name: "CMPGED", argLength: 2, reg: fp2flags, asm: "CMPGED", typ: "Flags"}, // flags=true if arg0 >= arg1, float64
   279  		{name: "CMPGTF", argLength: 2, reg: fp2flags, asm: "CMPGTF", typ: "Flags"}, // flags=true if arg0 > arg1, float32
   280  		{name: "CMPGTD", argLength: 2, reg: fp2flags, asm: "CMPGTD", typ: "Flags"}, // flags=true if arg0 > arg1, float64
   281  
   282  		// bitfield ops
   283  		// for bstrpick.w msbw is auxInt>>5, lsbw is auxInt&0x1f
   284  		// for bstrpick.d msbd is auxInt>>6, lsbd is auxInt&0x3f
   285  		{name: "BSTRPICKW", argLength: 1, reg: gp11, asm: "BSTRPICKW", aux: "Int64", earlyOk: true},
   286  		{name: "BSTRPICKV", argLength: 1, reg: gp11, asm: "BSTRPICKV", aux: "Int64", earlyOk: true},
   287  
   288  		// moves
   289  		{name: "MOVVconst", argLength: 0, reg: gp01, aux: "Int64", asm: "MOVV", typ: "UInt64", rematerializeable: true, earlyOk: true},    // auxint
   290  		{name: "MOVFconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVF", typ: "Float32", rematerializeable: true, earlyOk: true}, // auxint as 64-bit float, convert to 32-bit float
   291  		{name: "MOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVD", typ: "Float64", rematerializeable: true, earlyOk: true}, // auxint as 64-bit float
   292  
   293  		{name: "MOVVaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP").union(buildReg("SB"))}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVV", rematerializeable: true, symEffect: "Addr", earlyOk: true}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB
   294  
   295  		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},     // load from arg0 + auxInt + aux.  arg1=mem.
   296  		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
   297  		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},    // load from arg0 + auxInt + aux.  arg1=mem.
   298  		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true}, // load from arg0 + auxInt + aux.  arg1=mem.
   299  		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},    // load from arg0 + auxInt + aux.  arg1=mem.
   300  		{name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true}, // load from arg0 + auxInt + aux.  arg1=mem.
   301  		{name: "MOVVload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVV", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},   // load from arg0 + auxInt + aux.  arg1=mem.
   302  		{name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
   303  		{name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read", addrSinkArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
   304  
   305  		// register indexed load
   306  		{name: "MOVVloadidx", argLength: 3, reg: gp2load, asm: "MOVV", typ: "UInt64", addrSinkArg0: true},   // load 64-bit dword from arg0 + arg1, arg2 = mem.
   307  		{name: "MOVWloadidx", argLength: 3, reg: gp2load, asm: "MOVW", typ: "Int32", addrSinkArg0: true},    // load 32-bit word from arg0 + arg1, sign-extended to 64-bit, arg2=mem.
   308  		{name: "MOVWUloadidx", argLength: 3, reg: gp2load, asm: "MOVWU", typ: "UInt32", addrSinkArg0: true}, // load 32-bit word from arg0 + arg1, zero-extended to 64-bit, arg2=mem.
   309  		{name: "MOVHloadidx", argLength: 3, reg: gp2load, asm: "MOVH", typ: "Int16", addrSinkArg0: true},    // load 16-bit word from arg0 + arg1, sign-extended to 64-bit, arg2=mem.
   310  		{name: "MOVHUloadidx", argLength: 3, reg: gp2load, asm: "MOVHU", typ: "UInt16", addrSinkArg0: true}, // load 16-bit word from arg0 + arg1, zero-extended to 64-bit, arg2=mem.
   311  		{name: "MOVBloadidx", argLength: 3, reg: gp2load, asm: "MOVB", typ: "Int8", addrSinkArg0: true},     // load 8-bit word from arg0 + arg1, sign-extended to 64-bit, arg2=mem.
   312  		{name: "MOVBUloadidx", argLength: 3, reg: gp2load, asm: "MOVBU", typ: "UInt8", addrSinkArg0: true},  // load 8-bit word from arg0 + arg1, zero-extended to 64-bit, arg2=mem.
   313  		{name: "MOVFloadidx", argLength: 3, reg: fp2load, asm: "MOVF", typ: "Float32", addrSinkArg0: true},  // load 32-bit float from arg0 + arg1, arg2=mem.
   314  		{name: "MOVDloadidx", argLength: 3, reg: fp2load, asm: "MOVD", typ: "Float64", addrSinkArg0: true},  // load 64-bit float from arg0 + arg1, arg2=mem.
   315  
   316  		{name: "MOVBstore", argLength: 3, reg: regInfo{inputs: []regMask{gpspsbg, gpg.union(rz)}}, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true}, // store 1 byte of arg1 to arg0 + auxInt + aux.  arg2=mem.
   317  		{name: "MOVHstore", argLength: 3, reg: regInfo{inputs: []regMask{gpspsbg, gpg.union(rz)}}, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true}, // store 2 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   318  		{name: "MOVWstore", argLength: 3, reg: regInfo{inputs: []regMask{gpspsbg, gpg.union(rz)}}, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true}, // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   319  		{name: "MOVVstore", argLength: 3, reg: regInfo{inputs: []regMask{gpspsbg, gpg.union(rz)}}, aux: "SymOff", asm: "MOVV", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true}, // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   320  		{name: "MOVFstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVF", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true},                                            // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   321  		{name: "MOVDstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write", addrSinkArg0: true},                                            // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
   322  
   323  		// register indexed store
   324  		{name: "MOVBstoreidx", argLength: 4, reg: gpstore2, asm: "MOVB", typ: "Mem", addrSinkArg0: true}, // store 1 byte of arg2 to arg0 + arg1, arg3 = mem.
   325  		{name: "MOVHstoreidx", argLength: 4, reg: gpstore2, asm: "MOVH", typ: "Mem", addrSinkArg0: true}, // store 2 bytes of arg2 to arg0 + arg1, arg3 = mem.
   326  		{name: "MOVWstoreidx", argLength: 4, reg: gpstore2, asm: "MOVW", typ: "Mem", addrSinkArg0: true}, // store 4 bytes of arg2 to arg0 + arg1, arg3 = mem.
   327  		{name: "MOVVstoreidx", argLength: 4, reg: gpstore2, asm: "MOVV", typ: "Mem", addrSinkArg0: true}, // store 8 bytes of arg2 to arg0 + arg1, arg3 = mem.
   328  		{name: "MOVFstoreidx", argLength: 4, reg: fpstore2, asm: "MOVF", typ: "Mem", addrSinkArg0: true}, // store 32-bit float of arg2 to arg0 + arg1, arg3=mem.
   329  		{name: "MOVDstoreidx", argLength: 4, reg: fpstore2, asm: "MOVD", typ: "Mem", addrSinkArg0: true}, // store 64-bit float of arg2 to arg0 + arg1, arg3=mem.
   330  
   331  		// moves (no conversion)
   332  		{name: "MOVWfpgp", argLength: 1, reg: fpgp, asm: "MOVW", earlyOk: true}, // move float32 to int32 (no conversion).
   333  		{name: "MOVWgpfp", argLength: 1, reg: gpfp, asm: "MOVW", earlyOk: true}, // move int32 to float32 (no conversion).
   334  		{name: "MOVVfpgp", argLength: 1, reg: fpgp, asm: "MOVV", earlyOk: true}, // move float64 to int64 (no conversion).
   335  		{name: "MOVVgpfp", argLength: 1, reg: gpfp, asm: "MOVV", earlyOk: true}, // move int64 to float64 (no conversion).
   336  
   337  		// conversions
   338  		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB", earlyOk: true},   // move from arg0, sign-extended from byte
   339  		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU", earlyOk: true}, // move from arg0, unsign-extended from byte
   340  		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH", earlyOk: true},   // move from arg0, sign-extended from half
   341  		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU", earlyOk: true}, // move from arg0, unsign-extended from half
   342  		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW", earlyOk: true},   // move from arg0, sign-extended from word
   343  		{name: "MOVWUreg", argLength: 1, reg: gp11, asm: "MOVWU", earlyOk: true}, // move from arg0, unsign-extended from word
   344  		{name: "MOVVreg", argLength: 1, reg: gp11, asm: "MOVV", earlyOk: true},   // move from arg0
   345  
   346  		{name: "MOVVnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true, earlyOk: true}, // nop, return arg0 in same register
   347  
   348  		{name: "MOVWF", argLength: 1, reg: fp11, asm: "MOVWF", earlyOk: true},     // int32 -> float32
   349  		{name: "MOVWD", argLength: 1, reg: fp11, asm: "MOVWD", earlyOk: true},     // int32 -> float64
   350  		{name: "MOVVF", argLength: 1, reg: fp11, asm: "MOVVF", earlyOk: true},     // int64 -> float32
   351  		{name: "MOVVD", argLength: 1, reg: fp11, asm: "MOVVD", earlyOk: true},     // int64 -> float64
   352  		{name: "TRUNCFW", argLength: 1, reg: fp11, asm: "TRUNCFW", earlyOk: true}, // float32 -> int32
   353  		{name: "TRUNCDW", argLength: 1, reg: fp11, asm: "TRUNCDW", earlyOk: true}, // float64 -> int32
   354  		{name: "TRUNCFV", argLength: 1, reg: fp11, asm: "TRUNCFV", earlyOk: true}, // float32 -> int64
   355  		{name: "TRUNCDV", argLength: 1, reg: fp11, asm: "TRUNCDV", earlyOk: true}, // float64 -> int64
   356  		{name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD", earlyOk: true},     // float32 -> float64
   357  		{name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF", earlyOk: true},     // float64 -> float32
   358  
   359  		// 64-bit floating-point round to integers in 64-bit FP format
   360  		{name: "FRINTND", argLength: 1, reg: fp11, asm: "VFRINTRNED"}, // Round (ties to even; ; 0.5 -> 0, 1.5 -> 2)
   361  		{name: "FRINTMD", argLength: 1, reg: fp11, asm: "VFRINTRMD"},  // Floor (towards Minus; 0.5 -> 0, -0.5 -> -1)
   362  		{name: "FRINTPD", argLength: 1, reg: fp11, asm: "VFRINTRPD"},  // Ceil (towards Positive; 0.5 -> 1, -0.5 -> 0)
   363  		{name: "FRINTZD", argLength: 1, reg: fp11, asm: "VFRINTRZD"},  // Trunc (towards Zero; 0.5 -> 0, -0.5 -> 0))
   364  
   365  		// Round ops to block fused-multiply-add extraction.
   366  		{name: "LoweredRound32F", argLength: 1, reg: fp11, resultInArg0: true, earlyOk: true},
   367  		{name: "LoweredRound64F", argLength: 1, reg: fp11, resultInArg0: true, earlyOk: true},
   368  
   369  		// function calls
   370  		{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
   371  		{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
   372  		{name: "CALLtailinter", argLength: -1, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true},             // tail call fn by pointer.  arg0=codeptr, last arg=mem, auxint=argsize, returns mem
   373  		{name: "CALLclosure", argLength: -1, reg: regInfo{inputs: []regMask{gpsp, buildReg("R29"), regMask{}}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, last arg=mem, auxint=argsize, returns mem
   374  		{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
   375  
   376  		// medium zeroing
   377  		// arg0 = address of memory to zero
   378  		// arg1 = mem
   379  		// auxint = number of bytes to zero
   380  		// returns mem
   381  		{
   382  			name:      "LoweredZero",
   383  			aux:       "Int64",
   384  			argLength: 2,
   385  			reg: regInfo{
   386  				inputs: []regMask{gp},
   387  			},
   388  			faultOnNilArg0: true,
   389  			addrSinkArg0:   true,
   390  		},
   391  
   392  		// large zeroing
   393  		// arg0 = address of memory to zero
   394  		// arg1 = mem
   395  		// auxint = number of bytes to zero
   396  		// returns mem
   397  		{
   398  			name:      "LoweredZeroLoop",
   399  			aux:       "Int64",
   400  			argLength: 2,
   401  			reg: regInfo{
   402  				inputs:       []regMask{gp},
   403  				clobbers:     buildReg("F31"),
   404  				clobbersArg0: true,
   405  			},
   406  			faultOnNilArg0: true,
   407  			needIntTemp:    true,
   408  			addrSinkArg0:   true,
   409  		},
   410  
   411  		// medium copying
   412  		// arg0 = address of dst memory
   413  		// arg1 = address of src memory
   414  		// arg2 = mem
   415  		// auxint = number of bytes to copy
   416  		// returns mem
   417  		{
   418  			name:      "LoweredMove",
   419  			aux:       "Int64",
   420  			argLength: 3,
   421  			reg: regInfo{
   422  				inputs:   []regMask{gp.minus(buildReg("R23")), gp.minus(buildReg("R23"))},
   423  				clobbers: buildReg("R23"),
   424  			},
   425  			faultOnNilArg0: true,
   426  			faultOnNilArg1: true,
   427  			addrSinkArg0:   true,
   428  			addrSinkArg1:   true,
   429  		},
   430  
   431  		// large copying
   432  		// arg0 = address of dst memory
   433  		// arg1 = address of src memory
   434  		// arg2 = mem
   435  		// auxint = number of bytes to copy
   436  		// returns mem
   437  		{
   438  			name:      "LoweredMoveLoop",
   439  			aux:       "Int64",
   440  			argLength: 3,
   441  			reg: regInfo{
   442  				inputs:       []regMask{gp.minus(buildReg("R23 R24")), gp.minus(buildReg("R23 R24"))},
   443  				clobbers:     buildReg("R23 R24"),
   444  				clobbersArg0: true,
   445  				clobbersArg1: true,
   446  			},
   447  			faultOnNilArg0: true,
   448  			faultOnNilArg1: true,
   449  			addrSinkArg0:   true,
   450  			addrSinkArg1:   true,
   451  		},
   452  
   453  		// Atomic operations.
   454  		//
   455  		// resultNotInArgs is needed by all ops lowering to LoongArch
   456  		// atomic memory access instructions, because these instructions
   457  		// are defined to require rd != rj && rd != rk per the ISA spec.
   458  
   459  		// atomic loads.
   460  		// load from arg0. arg1=mem.
   461  		// returns <value,memory> so they can be properly ordered with other loads.
   462  		{name: "LoweredAtomicLoad8", argLength: 2, reg: gpload, faultOnNilArg0: true},
   463  		{name: "LoweredAtomicLoad32", argLength: 2, reg: gpload, faultOnNilArg0: true},
   464  		{name: "LoweredAtomicLoad64", argLength: 2, reg: gpload, faultOnNilArg0: true},
   465  
   466  		// atomic stores.
   467  		// store arg1 to arg0. arg2=mem. returns memory.
   468  		{name: "LoweredAtomicStore8", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   469  		{name: "LoweredAtomicStore32", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   470  		{name: "LoweredAtomicStore64", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   471  		{name: "LoweredAtomicStore8Variant", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   472  		{name: "LoweredAtomicStore32Variant", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   473  		{name: "LoweredAtomicStore64Variant", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   474  
   475  		// atomic exchange.
   476  		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>.
   477  		{name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   478  		{name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   479  
   480  		// atomic exchange variant.
   481  		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>. auxint must be zero.
   482  		// AMSWAPDBB   Rarg1, (Rarg0), Rout
   483  		{name: "LoweredAtomicExchange8Variant", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   484  
   485  		// atomic add.
   486  		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>.
   487  		{name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   488  		{name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   489  
   490  		// atomic compare and swap.
   491  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
   492  		// if *arg0 == arg1 {
   493  		//   *arg0 = arg2
   494  		//   return (true, memory)
   495  		// } else {
   496  		//   return (false, memory)
   497  		// }
   498  		// MOVV $0, Rout
   499  		// DBAR 0x14
   500  		// LL	(Rarg0), Rtmp
   501  		// BNE	Rtmp, Rarg1, 4(PC)
   502  		// MOVV Rarg2, Rout
   503  		// SC	Rout, (Rarg0)
   504  		// BEQ	Rout, -4(PC)
   505  		// DBAR 0x12
   506  		{name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   507  		{name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   508  
   509  		// atomic compare and swap variant.
   510  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory. auxint must be zero.
   511  		// if *arg0 == arg1 {
   512  		//   *arg0 = arg2
   513  		//   return (true, memory)
   514  		// } else {
   515  		//   return (false, memory)
   516  		// }
   517  		// MOVV         $0, Rout
   518  		// MOVV         Rarg1, Rtmp
   519  		// AMCASDBx     Rarg2, (Rarg0), Rtmp
   520  		// BNE          Rarg1, Rtmp, 2(PC)
   521  		// MOVV         $1, Rout
   522  		// NOP
   523  		{name: "LoweredAtomicCas64Variant", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   524  		{name: "LoweredAtomicCas32Variant", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   525  
   526  		// Atomic 32 bit AND/OR.
   527  		// *arg0 &= (|=) arg1. arg2=mem. returns nil.
   528  		{name: "LoweredAtomicAnd32", argLength: 3, reg: gpoldatom, asm: "AMANDDBW", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   529  		{name: "LoweredAtomicOr32", argLength: 3, reg: gpoldatom, asm: "AMORDBW", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   530  
   531  		// Atomic 32,64 bit AND/OR.
   532  		// *arg0 &= (|=) arg1. arg2=mem. returns <old content of *arg0, memory>. auxint must be zero.
   533  		{name: "LoweredAtomicAnd32value", argLength: 3, reg: gpxchg, asm: "AMANDDBW", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   534  		{name: "LoweredAtomicAnd64value", argLength: 3, reg: gpxchg, asm: "AMANDDBV", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   535  		{name: "LoweredAtomicOr32value", argLength: 3, reg: gpxchg, asm: "AMORDBW", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   536  		{name: "LoweredAtomicOr64value", argLength: 3, reg: gpxchg, asm: "AMORDBV", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   537  
   538  		// pseudo-ops
   539  		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil.  arg1=mem.
   540  
   541  		{name: "FPFlagTrue", argLength: 1, reg: readflags, earlyOk: true},  // bool, true if FP flag is true
   542  		{name: "FPFlagFalse", argLength: 1, reg: readflags, earlyOk: true}, // bool, true if FP flag is false
   543  
   544  		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   545  		// and sorts it to the very beginning of the block to prevent other
   546  		// use of R22 (loong64.REGCTXT, the closure pointer)
   547  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R29")}}, zeroWidth: true},
   548  
   549  		// LoweredGetCallerSP returns the SP of the caller of the current function. arg0=mem.
   550  		{name: "LoweredGetCallerSP", argLength: 1, reg: gp01, rematerializeable: true},
   551  
   552  		// LoweredGetCallerPC evaluates to the PC to which its "caller" will return.
   553  		// I.e., if f calls g "calls" sys.GetCallerPC,
   554  		// the result should be the PC within f that g will return to.
   555  		// See runtime/stubs.go for a more detailed discussion.
   556  		{name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true},
   557  
   558  		// LoweredWB invokes runtime.gcWriteBarrier. arg0=mem, auxint=# of buffer entries needed
   559  		// It saves all GP registers if necessary,
   560  		// but clobbers R1 (LR) because it's a call
   561  		// and R30 (REGTMP).
   562  		// Returns a pointer to a write barrier buffer in R29.
   563  		{name: "LoweredWB", argLength: 1, reg: regInfo{clobbers: callerSave.minus(gpg).union(buildReg("R1")), outputs: []regMask{buildReg("R29")}}, clobberFlags: true, aux: "Int64"},
   564  
   565  		// Do data barrier. arg0=memorys
   566  		{name: "LoweredPubBarrier", argLength: 1, asm: "DBAR", hasSideEffects: true},
   567  
   568  		// LoweredPanicBoundsRR takes x and y, two values that caused a bounds check to fail.
   569  		// the RC and CR versions are used when one of the arguments is a constant. CC is used
   570  		// when both are constant (normally both 0, as prove derives the fact that a [0] bounds
   571  		// failure means the length must have also been 0).
   572  		// AuxInt contains a report code (see PanicBounds in genericOps.go).
   573  		{name: "LoweredPanicBoundsRR", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{first16, first16}}, typ: "Mem", call: true}, // arg0=x, arg1=y, arg2=mem, returns memory.
   574  		{name: "LoweredPanicBoundsRC", argLength: 2, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{first16}}, typ: "Mem", call: true},   // arg0=x, arg1=mem, returns memory.
   575  		{name: "LoweredPanicBoundsCR", argLength: 2, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{first16}}, typ: "Mem", call: true},   // arg0=y, arg1=mem, returns memory.
   576  		{name: "LoweredPanicBoundsCC", argLength: 1, aux: "PanicBoundsCC", reg: regInfo{}, typ: "Mem", call: true},                            // arg0=mem, returns memory.
   577  
   578  		// Prefetch instruction
   579  		// Do prefetch arg0 address with option aux. arg0=addr, arg1=memory, aux=option.
   580  		// Note:
   581  		//   The aux of PRELDX is actually composed of two values: $hint and $n. bit[4:0]
   582  		//   is $hint and bit[41:5] is $n.
   583  		{name: "PRELD", argLength: 2, aux: "Int64", reg: preldreg, asm: "PRELD", hasSideEffects: true},
   584  		{name: "PRELDX", argLength: 2, aux: "Int64", reg: preldreg, asm: "PRELDX", hasSideEffects: true},
   585  
   586  		{name: "ADDshiftLLV", argLength: 2, aux: "Int64", reg: gp21, asm: "ALSLV", earlyOk: true}, // arg0 + arg1<<auxInt, the value of auxInt should be in the range [1, 4].
   587  		{name: "ZERO", zeroWidth: true, fixedReg: true, earlyOk: true},
   588  	}
   589  
   590  	blocks := []blockData{
   591  		{name: "EQZ", controls: 1},  // = 0
   592  		{name: "NEZ", controls: 1},  // != 0
   593  		{name: "LTZ", controls: 1},  // < 0
   594  		{name: "LEZ", controls: 1},  // <= 0
   595  		{name: "GTZ", controls: 1},  // > 0
   596  		{name: "GEZ", controls: 1},  // >= 0
   597  		{name: "FPT", controls: 1},  // FP flag is true
   598  		{name: "FPF", controls: 1},  // FP flag is false
   599  		{name: "BEQ", controls: 2},  // controls[0] == controls[1]
   600  		{name: "BNE", controls: 2},  // controls[0] != controls[1]
   601  		{name: "BGE", controls: 2},  // controls[0] >= controls[1]
   602  		{name: "BLT", controls: 2},  // controls[0] < controls[1]
   603  		{name: "BGEU", controls: 2}, // controls[0] >= controls[1], unsigned
   604  		{name: "BLTU", controls: 2}, // controls[0] < controls[1], unsigned
   605  
   606  		// JUMPTABLE implements jump tables.
   607  		// Aux is the symbol (an *obj.LSym) for the jump table.
   608  		// control[0] is the index into the jump table.
   609  		// control[1] is the address of the jump table (the address of the symbol stored in Aux).
   610  		{name: "JUMPTABLE", controls: 2, aux: "Sym"},
   611  	}
   612  
   613  	archs = append(archs, arch{
   614  		name:     "LOONG64",
   615  		pkg:      "cmd/internal/obj/loong64",
   616  		genfile:  "../../loong64/ssa.go",
   617  		ops:      ops,
   618  		blocks:   blocks,
   619  		regnames: regNamesLOONG64,
   620  		// TODO: support register ABI on loong64
   621  		ParamIntRegNames:   "R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19",
   622  		ParamFloatRegNames: "F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15",
   623  		gpregmask:          gp,
   624  		fpregmask:          fp,
   625  		framepointerreg:    -1, // not used
   626  		linkreg:            int8(num["R1"]),
   627  	})
   628  }
   629  

View as plain text