github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/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 R23 R24 R25 R26 R27 R28 R29 R31") // R1 is LR, R2 is thread pointer, R3 is stack pointer, R21-unused, R22 is g, R30 is REGTMP
   127  		gps        = buildReg("R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R19 R20 R23 R24 R25 R26 R27 R28 R29 R31") | buildReg("g")
   128  		gpg        = gp | buildReg("g")
   129  		gpsp       = gp | buildReg("SP")
   130  		gpspg      = gpg | buildReg("SP")
   131  		gpspsbg    = gpspg | buildReg("SB")
   132  		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")
   133  		callerSave = gp | fp | buildReg("g") // runtime.setg (and anything calling it) may clobber g
   134  		r1         = buildReg("R19")
   135  		r2         = buildReg("R18")
   136  		r3         = buildReg("R17")
   137  		r4         = buildReg("R4")
   138  	)
   139  	// Common regInfo
   140  	var (
   141  		gp01      = regInfo{inputs: nil, outputs: []regMask{gp}}
   142  		gp11      = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
   143  		gp11sp    = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
   144  		gp21      = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}}
   145  		gp22      = regInfo{inputs: []regMask{gps, gps}, outputs: []regMask{gp, gp}}
   146  		gpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}
   147  		gpstore   = regInfo{inputs: []regMask{gpspsbg, gpg}}
   148  		gpstore0  = regInfo{inputs: []regMask{gpspsbg}}
   149  		gpxchg    = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
   150  		gpcas     = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}}
   151  		fp01      = regInfo{inputs: nil, outputs: []regMask{fp}}
   152  		fp11      = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
   153  		fp21      = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
   154  		fp2flags  = regInfo{inputs: []regMask{fp, fp}}
   155  		fpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
   156  		fpstore   = regInfo{inputs: []regMask{gpspsbg, fp}}
   157  		readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
   158  	)
   159  	ops := []opData{
   160  		// binary ops
   161  		{name: "ADDV", argLength: 2, reg: gp21, asm: "ADDVU", commutative: true},   // arg0 + arg1
   162  		{name: "ADDVconst", argLength: 1, reg: gp11sp, asm: "ADDVU", aux: "Int64"}, // arg0 + auxInt. auxInt is 32-bit, also in other *const ops.
   163  		{name: "SUBV", argLength: 2, reg: gp21, asm: "SUBVU"},                      // arg0 - arg1
   164  		{name: "SUBVconst", argLength: 1, reg: gp11, asm: "SUBVU", aux: "Int64"},   // arg0 - auxInt
   165  
   166  		{name: "MULV", argLength: 2, reg: gp22, resultNotInArgs: true, commutative: true, typ: "(Int64,Int64)"},    // arg0 * arg1, signed
   167  		{name: "MULVU", argLength: 2, reg: gp22, resultNotInArgs: true, commutative: true, typ: "(UInt64,UInt64)"}, // arg0 * arg1, unsigned
   168  		{name: "DIVV", argLength: 2, reg: gp22, resultNotInArgs: true, typ: "(Int64,Int64)"},                       // arg0 / arg1, signed
   169  		{name: "DIVVU", argLength: 2, reg: gp22, resultNotInArgs: true, typ: "(UInt64,UInt64)"},                    // arg0 / arg1, unsigned
   170  
   171  		{name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true}, // arg0 + arg1
   172  		{name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true}, // arg0 + arg1
   173  		{name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF"},                    // arg0 - arg1
   174  		{name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD"},                    // arg0 - arg1
   175  		{name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true}, // arg0 * arg1
   176  		{name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true}, // arg0 * arg1
   177  		{name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF"},                    // arg0 / arg1
   178  		{name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD"},                    // arg0 / arg1
   179  
   180  		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true},                // arg0 & arg1
   181  		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64"},                // arg0 & auxInt
   182  		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true},                  // arg0 | arg1
   183  		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64"},                  // arg0 | auxInt
   184  		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, typ: "UInt64"}, // arg0 ^ arg1
   185  		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64", typ: "UInt64"}, // arg0 ^ auxInt
   186  		{name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true},                // ^(arg0 | arg1)
   187  		{name: "NORconst", argLength: 1, reg: gp11, asm: "NOR", aux: "Int64"},                // ^(arg0 | auxInt)
   188  
   189  		{name: "NEGV", argLength: 1, reg: gp11},                // -arg0
   190  		{name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF"},   // -arg0, float32
   191  		{name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD"},   // -arg0, float64
   192  		{name: "SQRTD", argLength: 1, reg: fp11, asm: "SQRTD"}, // sqrt(arg0), float64
   193  		{name: "SQRTF", argLength: 1, reg: fp11, asm: "SQRTF"}, // sqrt(arg0), float32
   194  
   195  		{name: "MASKEQZ", argLength: 2, reg: gp21, asm: "MASKEQZ"}, // returns 0 if arg1 == 0, otherwise returns arg0
   196  		{name: "MASKNEZ", argLength: 2, reg: gp21, asm: "MASKNEZ"}, // returns 0 if arg1 != 0, otherwise returns arg0
   197  
   198  		// shifts
   199  		{name: "SLLV", argLength: 2, reg: gp21, asm: "SLLV"},                      // arg0 << arg1, shift amount is mod 64
   200  		{name: "SLLVconst", argLength: 1, reg: gp11, asm: "SLLV", aux: "Int64"},   // arg0 << auxInt
   201  		{name: "SRLV", argLength: 2, reg: gp21, asm: "SRLV"},                      // arg0 >> arg1, unsigned, shift amount is mod 64
   202  		{name: "SRLVconst", argLength: 1, reg: gp11, asm: "SRLV", aux: "Int64"},   // arg0 >> auxInt, unsigned
   203  		{name: "SRAV", argLength: 2, reg: gp21, asm: "SRAV"},                      // arg0 >> arg1, signed, shift amount is mod 64
   204  		{name: "SRAVconst", argLength: 1, reg: gp11, asm: "SRAV", aux: "Int64"},   // arg0 >> auxInt, signed
   205  		{name: "ROTR", argLength: 2, reg: gp21, asm: "ROTR"},                      // arg0 right rotate by (arg1 mod 32) bits
   206  		{name: "ROTRV", argLength: 2, reg: gp21, asm: "ROTRV"},                    // arg0 right rotate by (arg1 mod 64) bits
   207  		{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.
   208  		{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.
   209  
   210  		// comparisons
   211  		{name: "SGT", argLength: 2, reg: gp21, asm: "SGT", typ: "Bool"},                      // 1 if arg0 > arg1 (signed), 0 otherwise
   212  		{name: "SGTconst", argLength: 1, reg: gp11, asm: "SGT", aux: "Int64", typ: "Bool"},   // 1 if auxInt > arg0 (signed), 0 otherwise
   213  		{name: "SGTU", argLength: 2, reg: gp21, asm: "SGTU", typ: "Bool"},                    // 1 if arg0 > arg1 (unsigned), 0 otherwise
   214  		{name: "SGTUconst", argLength: 1, reg: gp11, asm: "SGTU", aux: "Int64", typ: "Bool"}, // 1 if auxInt > arg0 (unsigned), 0 otherwise
   215  
   216  		{name: "CMPEQF", argLength: 2, reg: fp2flags, asm: "CMPEQF", typ: "Flags"}, // flags=true if arg0 = arg1, float32
   217  		{name: "CMPEQD", argLength: 2, reg: fp2flags, asm: "CMPEQD", typ: "Flags"}, // flags=true if arg0 = arg1, float64
   218  		{name: "CMPGEF", argLength: 2, reg: fp2flags, asm: "CMPGEF", typ: "Flags"}, // flags=true if arg0 >= arg1, float32
   219  		{name: "CMPGED", argLength: 2, reg: fp2flags, asm: "CMPGED", typ: "Flags"}, // flags=true if arg0 >= arg1, float64
   220  		{name: "CMPGTF", argLength: 2, reg: fp2flags, asm: "CMPGTF", typ: "Flags"}, // flags=true if arg0 > arg1, float32
   221  		{name: "CMPGTD", argLength: 2, reg: fp2flags, asm: "CMPGTD", typ: "Flags"}, // flags=true if arg0 > arg1, float64
   222  
   223  		// moves
   224  		{name: "MOVVconst", argLength: 0, reg: gp01, aux: "Int64", asm: "MOVV", typ: "UInt64", rematerializeable: true},    // auxint
   225  		{name: "MOVFconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVF", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float
   226  		{name: "MOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float
   227  
   228  		{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
   229  
   230  		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"},     // load from arg0 + auxInt + aux.  arg1=mem.
   231  		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   232  		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"},    // load from arg0 + auxInt + aux.  arg1=mem.
   233  		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux.  arg1=mem.
   234  		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true, symEffect: "Read"},    // load from arg0 + auxInt + aux.  arg1=mem.
   235  		{name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux.  arg1=mem.
   236  		{name: "MOVVload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVV", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"},   // load from arg0 + auxInt + aux.  arg1=mem.
   237  		{name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   238  		{name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"},  // load from arg0 + auxInt + aux.  arg1=mem.
   239  
   240  		{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.
   241  		{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.
   242  		{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.
   243  		{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.
   244  		{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.
   245  		{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.
   246  
   247  		{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.
   248  		{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.
   249  		{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.
   250  		{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.
   251  
   252  		// conversions
   253  		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"},   // move from arg0, sign-extended from byte
   254  		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte
   255  		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"},   // move from arg0, sign-extended from half
   256  		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half
   257  		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"},   // move from arg0, sign-extended from word
   258  		{name: "MOVWUreg", argLength: 1, reg: gp11, asm: "MOVWU"}, // move from arg0, unsign-extended from word
   259  		{name: "MOVVreg", argLength: 1, reg: gp11, asm: "MOVV"},   // move from arg0
   260  
   261  		{name: "MOVVnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register
   262  
   263  		{name: "MOVWF", argLength: 1, reg: fp11, asm: "MOVWF"},     // int32 -> float32
   264  		{name: "MOVWD", argLength: 1, reg: fp11, asm: "MOVWD"},     // int32 -> float64
   265  		{name: "MOVVF", argLength: 1, reg: fp11, asm: "MOVVF"},     // int64 -> float32
   266  		{name: "MOVVD", argLength: 1, reg: fp11, asm: "MOVVD"},     // int64 -> float64
   267  		{name: "TRUNCFW", argLength: 1, reg: fp11, asm: "TRUNCFW"}, // float32 -> int32
   268  		{name: "TRUNCDW", argLength: 1, reg: fp11, asm: "TRUNCDW"}, // float64 -> int32
   269  		{name: "TRUNCFV", argLength: 1, reg: fp11, asm: "TRUNCFV"}, // float32 -> int64
   270  		{name: "TRUNCDV", argLength: 1, reg: fp11, asm: "TRUNCDV"}, // float64 -> int64
   271  		{name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD"},     // float32 -> float64
   272  		{name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"},     // float64 -> float32
   273  
   274  		// function calls
   275  		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                               // call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
   276  		{name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true},                                 // tail call static function aux.(*obj.LSym).  arg0=mem, auxint=argsize, returns mem
   277  		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R29"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
   278  		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                         // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
   279  
   280  		// duffzero
   281  		// arg0 = address of memory to zero
   282  		// arg1 = mem
   283  		// auxint = offset into duffzero code to start executing
   284  		// returns mem
   285  		// R19 aka loong64.REGRT1 changed as side effect
   286  		{
   287  			name:      "DUFFZERO",
   288  			aux:       "Int64",
   289  			argLength: 2,
   290  			reg: regInfo{
   291  				inputs:   []regMask{gp},
   292  				clobbers: buildReg("R19 R1"),
   293  			},
   294  			faultOnNilArg0: true,
   295  		},
   296  
   297  		// duffcopy
   298  		// arg0 = address of dst memory (in R20, changed as side effect) REGRT2
   299  		// arg1 = address of src memory (in R19, changed as side effect) REGRT1
   300  		// arg2 = mem
   301  		// auxint = offset into duffcopy code to start executing
   302  		// returns mem
   303  		{
   304  			name:      "DUFFCOPY",
   305  			aux:       "Int64",
   306  			argLength: 3,
   307  			reg: regInfo{
   308  				inputs:   []regMask{buildReg("R20"), buildReg("R19")},
   309  				clobbers: buildReg("R19 R20 R1"),
   310  			},
   311  			faultOnNilArg0: true,
   312  			faultOnNilArg1: true,
   313  		},
   314  
   315  		// large or unaligned zeroing
   316  		// arg0 = address of memory to zero (in R19, changed as side effect)
   317  		// arg1 = address of the last element to zero
   318  		// arg2 = mem
   319  		// auxint = alignment
   320  		// returns mem
   321  		//	SUBV	$8, R19
   322  		//	MOVV	R0, 8(R19)
   323  		//	ADDV	$8, R19
   324  		//	BNE	Rarg1, R19, -2(PC)
   325  		{
   326  			name:      "LoweredZero",
   327  			aux:       "Int64",
   328  			argLength: 3,
   329  			reg: regInfo{
   330  				inputs:   []regMask{buildReg("R19"), gp},
   331  				clobbers: buildReg("R19"),
   332  			},
   333  			clobberFlags:   true,
   334  			faultOnNilArg0: true,
   335  		},
   336  
   337  		// large or unaligned move
   338  		// arg0 = address of dst memory (in R4, changed as side effect)
   339  		// arg1 = address of src memory (in R19, changed as side effect)
   340  		// arg2 = address of the last element of src
   341  		// arg3 = mem
   342  		// auxint = alignment
   343  		// returns mem
   344  		//	SUBV	$8, R19
   345  		//	MOVV	8(R19), Rtmp
   346  		//	MOVV	Rtmp, (R4)
   347  		//	ADDV	$8, R19
   348  		//	ADDV	$8, R4
   349  		//	BNE	Rarg2, R19, -4(PC)
   350  		{
   351  			name:      "LoweredMove",
   352  			aux:       "Int64",
   353  			argLength: 4,
   354  			reg: regInfo{
   355  				inputs:   []regMask{buildReg("R4"), buildReg("R19"), gp},
   356  				clobbers: buildReg("R19 R4"),
   357  			},
   358  			clobberFlags:   true,
   359  			faultOnNilArg0: true,
   360  			faultOnNilArg1: true,
   361  		},
   362  
   363  		// atomic loads.
   364  		// load from arg0. arg1=mem.
   365  		// returns <value,memory> so they can be properly ordered with other loads.
   366  		{name: "LoweredAtomicLoad8", argLength: 2, reg: gpload, faultOnNilArg0: true},
   367  		{name: "LoweredAtomicLoad32", argLength: 2, reg: gpload, faultOnNilArg0: true},
   368  		{name: "LoweredAtomicLoad64", argLength: 2, reg: gpload, faultOnNilArg0: true},
   369  
   370  		// atomic stores.
   371  		// store arg1 to arg0. arg2=mem. returns memory.
   372  		{name: "LoweredAtomicStore8", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   373  		{name: "LoweredAtomicStore32", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   374  		{name: "LoweredAtomicStore64", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   375  		// store zero to arg0. arg1=mem. returns memory.
   376  		{name: "LoweredAtomicStorezero32", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true},
   377  		{name: "LoweredAtomicStorezero64", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true},
   378  
   379  		// atomic exchange.
   380  		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>.
   381  		// DBAR
   382  		// LL	(Rarg0), Rout
   383  		// MOVV Rarg1, Rtmp
   384  		// SC	Rtmp, (Rarg0)
   385  		// BEQ	Rtmp, -3(PC)
   386  		// DBAR
   387  		{name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   388  		{name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   389  
   390  		// atomic add.
   391  		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>.
   392  		// DBAR
   393  		// LL	(Rarg0), Rout
   394  		// ADDV Rarg1, Rout, Rtmp
   395  		// SC	Rtmp, (Rarg0)
   396  		// BEQ	Rtmp, -3(PC)
   397  		// DBAR
   398  		// ADDV Rarg1, Rout
   399  		{name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   400  		{name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   401  		// *arg0 += auxint. arg1=mem. returns <new content of *arg0, memory>. auxint is 32-bit.
   402  		{name: "LoweredAtomicAddconst32", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int32", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   403  		{name: "LoweredAtomicAddconst64", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int64", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   404  
   405  		// atomic compare and swap.
   406  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
   407  		// if *arg0 == arg1 {
   408  		//   *arg0 = arg2
   409  		//   return (true, memory)
   410  		// } else {
   411  		//   return (false, memory)
   412  		// }
   413  		// DBAR
   414  		// MOVV $0, Rout
   415  		// LL	(Rarg0), Rtmp
   416  		// BNE	Rtmp, Rarg1, 4(PC)
   417  		// MOVV Rarg2, Rout
   418  		// SC	Rout, (Rarg0)
   419  		// BEQ	Rout, -4(PC)
   420  		// DBAR
   421  		{name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   422  		{name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   423  
   424  		// pseudo-ops
   425  		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil.  arg1=mem.
   426  
   427  		{name: "FPFlagTrue", argLength: 1, reg: readflags},  // bool, true if FP flag is true
   428  		{name: "FPFlagFalse", argLength: 1, reg: readflags}, // bool, true if FP flag is false
   429  
   430  		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   431  		// and sorts it to the very beginning of the block to prevent other
   432  		// use of R22 (loong64.REGCTXT, the closure pointer)
   433  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R29")}}, zeroWidth: true},
   434  
   435  		// LoweredGetCallerSP returns the SP of the caller of the current function.
   436  		{name: "LoweredGetCallerSP", reg: gp01, rematerializeable: true},
   437  
   438  		// LoweredGetCallerPC evaluates to the PC to which its "caller" will return.
   439  		// I.e., if f calls g "calls" getcallerpc,
   440  		// the result should be the PC within f that g will return to.
   441  		// See runtime/stubs.go for a more detailed discussion.
   442  		{name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true},
   443  
   444  		// LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier
   445  		// It saves all GP registers if necessary,
   446  		// but clobbers R1 (LR) because it's a call
   447  		// and R30 (REGTMP).
   448  		{name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("R27"), buildReg("R28")}, clobbers: (callerSave &^ gpg) | buildReg("R1")}, clobberFlags: true, aux: "Sym", symEffect: "None"},
   449  
   450  		// There are three of these functions so that they can have three different register inputs.
   451  		// When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the
   452  		// default registers to match so we don't need to copy registers around unnecessarily.
   453  		{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).
   454  		{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).
   455  		{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).
   456  	}
   457  
   458  	blocks := []blockData{
   459  		{name: "EQ", controls: 1},
   460  		{name: "NE", controls: 1},
   461  		{name: "LTZ", controls: 1}, // < 0
   462  		{name: "LEZ", controls: 1}, // <= 0
   463  		{name: "GTZ", controls: 1}, // > 0
   464  		{name: "GEZ", controls: 1}, // >= 0
   465  		{name: "FPT", controls: 1}, // FP flag is true
   466  		{name: "FPF", controls: 1}, // FP flag is false
   467  	}
   468  
   469  	archs = append(archs, arch{
   470  		name:     "LOONG64",
   471  		pkg:      "cmd/internal/obj/loong64",
   472  		genfile:  "../../loong64/ssa.go",
   473  		ops:      ops,
   474  		blocks:   blocks,
   475  		regnames: regNamesLOONG64,
   476  		// TODO: support register ABI on loong64
   477  		ParamIntRegNames:   "R4 R5 R6 R7 R8 R9 R10 R11",
   478  		ParamFloatRegNames: "F0 F1 F2 F3 F4 F5 F6 F7",
   479  		gpregmask:          gp,
   480  		fpregmask:          fp,
   481  		framepointerreg:    -1, // not used
   482  		linkreg:            int8(num["R1"]),
   483  	})
   484  }